/******************************************************************************
 * Name         : drawvarray.c
 * Author       : 
 * Created      : 
 *
 * Copyright    : 2006-2009 by Imagination Technologies Limited.
 *              : All rights reserved. No part of this software, either
 *              : material or conceptual may be copied or distributed,
 *              : transmitted, transcribed, stored in a retrieval system or
 *              : translated into any human or computer language in any form
 *              : by any means, electronic, mechanical, manual or otherwise,
 *              : or disclosed to third parties without the express written
 *              : permission of Imagination Technologies Limited,
 *              : Home Park Estate, Kings Langley, Hertfordshire,
 *              : WD4 8LZ, U.K.
 *
 * Description  : All functions associated with vertex arrays
 *                Based on np2varray.c 1.40 and pmxvarray.c 1.170
 *
 * Platform     : ANSI
 *
 * Modifications:-
 * $Log: drawvarray.c $
 * 
 *  --- Revision Logs Removed --- 
 * 
 *  --- Revision Logs Removed --- 
 *****************************************************************************/

#include "context.h"
#include "osglue.h"

#define GLES2_NUM_STATIC_INDICES 1024


/* Whether the primitive eType is natively supported by the hardware */
static const IMG_BOOL primDirectIndex[7] =
{
	IMG_TRUE,  /* GL_POINTS         */
	IMG_TRUE,  /* GL_LINES          */
	IMG_FALSE, /* GL_LINE_LOOP      */
	IMG_FALSE, /* GL_LINE_STRIP     */
	IMG_TRUE,  /* GL_TRIANGLES      */
	IMG_TRUE,  /* GL_TRIANGLE_STRIP */
	IMG_TRUE   /* GL_TRIANGLE_FAN   */
};

/* Number of indices required by the hardware per GL index (see primDirectIndex) */
static const IMG_UINT32 primIndexMult[7] =
{
	1,         /* GL_POINTS         */
	1,         /* GL_LINES          */
	2,         /* GL_LINE_LOOP      */
	2,         /* GL_LINE_STRIP     */
	1,         /* GL_TRIANGLES      */
	1,         /* GL_TRIANGLE_STRIP */
	1,         /* GL_TRIANGLE_FAN   */
};

/* Defined as a percentage of the buffer in bytes */
#define MAX_VBUFFER (gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32SingleKickLimitInBytes - 4)

#if defined(FIX_HW_BRN_23946)
	#define MAX_INDEX_ADDRESS		(8 * 1024 * 1024)
#else
	#define MAX_INDEX_ADDRESS		(16 * 1024 * 1024)
#endif

#define MAX_IBUFFER	(MIN(gc->apsBuffers[CBUF_TYPE_INDEX_DATA_BUFFER]->ui32SingleKickLimitInBytes - 4, MAX_INDEX_ADDRESS))
#define MAX_INDICES_PER_PRIMBLOCK(indexsize) (MIN((MAX_INDEX_ADDRESS / (indexsize)), ~EURASIA_VDM_IDXCOUNT_CLRMSK))

typedef IMG_VOID (*PFNDrawVArray)(GLES2Context *, GLenum, IMG_UINT32, IMG_UINT32, IMG_UINT32, GLenum, const IMG_VOID *, IMG_UINT32, IMG_UINT32);

#define ALIGN_32BIT_INDICES(pui32Indices) pui32Indices = (IMG_UINT32 *)(((IMG_UINTPTR_T)pui32Indices + 0x3) & ~0x3)

/* Implementations of glDrawArrays/glDrawElements in decreasing order of performance.

   1- DrawElementsIndexBO. The indices come from a buffer object.
      1.1- Only supports DrawElements.
      1.2- The primitive type must be supported by the hardware.
      1.3- The indices must not be 8-bit.
      1.4- The vertices must fit in the buffer.
      1.5- The indices must come from an buffer object.

   2- DrawArraysAutoIndices. Writes consecutive 16-bit indices.
      2.1- Only supports DrawArrays.
      2.2- The primitive type must be supported by the hardware.
      2.3- The vertices must fit in the buffer.
      2.4- The (autogenerated 16-bit) indices must fit in the buffer.

   3- DrawElementsDeindex. De-indexes the vertices. Reads 16 or 32-bit indices. Generates 16-bit indices.
      4.1- Only meaningful for DrawElements.
      4.2- The deindexed vertices must fit in the buffer.
      4.3- The autogenerated indices must fit in the buffer.

   4- DrawVertexArray. The input indices, if present, can be 16-bit or 32-bit.
      Supports DrawArrays and DrawElements. Memcopies the indices every time it's possible.

   5- DrawBatchOnIBuffer. Sends indices in batches. XXX: Not supported at the moment.
      6.1- The vertices must fit in the buffer.

   6- DrawBatchOnVBuffer. De-indexes vertices and sends them in batches using 16-bit indices.
      The input indices, if any, can be either 16 or 32-bit.
*/


/***********************************************************************************
 Function Name      : WriteDirect16Indices
 Inputs             : gc
                      eMode      - One of GL_POINTS, GL_LINES, GL_TRIANGLES,
                                  GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN.
                      ui32First - Position of the first index to use
                      ui32Count - Number of indices to use
                      pvIndices - Actual indices or NULL for autogenerated consecutive indices.
 Outputs            : -
 Returns            : -
 Description        : Writes 16-bit indices for point, line, triangles,
 					    triangle-strip and triangle-fan primitives into the
 					    index buffer and makes the call to emit the primitive state.
************************************************************************************/
static IMG_VOID WriteDirect16Indices(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32First,
									 IMG_UINT32 ui32Count, const IMG_VOID *pvIndices)
{
	const IMG_UINT16 *pui16Indices = (const IMG_UINT16 *)pvIndices;
	IMG_UINT16 * IMG_RESTRICT pui16IndicesOut;
	IMG_UINT32 ui32IndexDWords;

	switch(eMode)
	{
		case GL_POINTS:
		{
			if (ui32Count<1)
			{
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
				return;
			}

			break;
		}
		case GL_LINES:
		{
			/* Make it multiple of 2 */
			ui32Count = ui32Count - ui32Count%2;

			if (ui32Count<2)
			{
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
				return;
			}

			break;
		}
		case GL_TRIANGLES:
		{
			/* Make it multiple of 3 and fall through */
			ui32Count = ui32Count - ui32Count%3;
		}
		case GL_TRIANGLE_STRIP:
		case GL_TRIANGLE_FAN:
		{
			if (ui32Count<3)
			{
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
				return;
			} 

			break;
		}
		default :
		{
			/* Can't handle GL_LINE_STRIP or GL_LINE_LOOP in this function */
			PVR_DPF((PVR_DBG_ERROR,"WriteDirect16Indices: Bad eMode (0x%X) - shouldn't be here", eMode));
			return;
		}
	}

	pui16IndicesOut = (IMG_UINT16 *)gc->pui32IndexData;
	
	GLES2_TIME_START(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

	if (pui16Indices)
	{
		GLES2MemCopy(pui16IndicesOut, &pui16Indices[ui32First], sizeof(IMG_UINT16)*ui32Count);
	}
	else
	{
		IMG_UINT32 i;

		for (i=ui32First; i < ui32Count + ui32First; ++i)
		{
			*pui16IndicesOut++ = (IMG_UINT16)i;
		}
	}
	
	GLES2_TIME_STOP(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

	/* Round up mem usage to nearest 32 bits */
	ui32IndexDWords = (ui32Count + 1) >> 1;

	CBUF_UpdateBufferPos(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER);

	GLES2_INC_COUNT(GLES2_TIMER_INDEX_DATA_COUNT, ui32IndexDWords);

	/* Emit state */
	GLES2EmitState(gc, eMode, IMG_FALSE, ui32Count,
	               CBUF_GetBufferDeviceAddress(gc->apsBuffers, gc->pui32IndexData, CBUF_TYPE_INDEX_DATA_BUFFER), 0);

}

/***********************************************************************************
 Function Name      : WriteLineStripOrLoop16Indices
 Inputs             : gc
                      eMode      - GL_LINE_STRIP or GL_LINE_LOOP
                      ui32First - Position of the first index to use
                      ui32Count - Number of indices to use
                      pvIndices - Actual indices or NULL for autogenerated consecutive indices.
 Outputs            : 
 Returns            : 
 Description        : Writes 16-bit indices for line strip/loop primitives into the index buffer
					    and makes the call to emit the primitive state.
************************************************************************************/
static IMG_VOID WriteLineStripOrLoop16Indices(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32First,
									   IMG_UINT32 ui32Count, const IMG_VOID *pvIndices)
{
	const IMG_UINT16 *pui16Indices = (const IMG_UINT16 *)pvIndices;
	IMG_UINT16 * IMG_RESTRICT pui16IndicesOut;
	IMG_UINT32 i, ui32NumIndices, ui32IndexDWords;
	IMG_UINT16 i0, i1;

	GLES_ASSERT( (eMode == GL_LINE_STRIP) || (eMode == GL_LINE_LOOP) );
	GLES_ASSERT( ui32First + ui32Count < 64*1024 );

	if (ui32Count<2)
	{
		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
		return;
	}
	
	pui16IndicesOut = (IMG_UINT16 *)gc->pui32IndexData;
	
	GLES2_TIME_START(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

	if (pui16Indices)
	{
		/* Copy the indices with replication to emulate a line strip using line segments */
		for (i=ui32First; i < ui32Count + ui32First - 1; pui16IndicesOut += 2)
		{
			i0 = pui16Indices[i++];
			i1 = pui16Indices[i];

			pui16IndicesOut[0] = i0;
			pui16IndicesOut[1] = i1;
		}

		i0 = pui16Indices[i];
		i1 = pui16Indices[ui32First];
	}
	else
	{
		/* Generate the indices with replication to emulate a line strip using line segments */
		for (i=ui32First; i <  ui32Count + ui32First - 1; pui16IndicesOut += 2, i++)
		{
			i0 = (IMG_UINT16)i;
			i1 = (IMG_UINT16)(i+1);

			pui16IndicesOut[0] = (IMG_UINT16)i0;
			pui16IndicesOut[1] = (IMG_UINT16)i1;
		}

		i0 = (IMG_UINT16)i;
		i1 = (IMG_UINT16)ui32First;
	}

	if((eMode == GL_LINE_LOOP) && (i0 != i1))
	{
		/* Add the connection from the last to the first vertex to close the loop */
		pui16IndicesOut[0] = i0;
		pui16IndicesOut[1] = i1;
		pui16IndicesOut += 2;
	}
	
	GLES2_TIME_STOP(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

	ui32NumIndices = (IMG_UINT32)(pui16IndicesOut - (IMG_UINT16 *)gc->pui32IndexData);

	/* Round up to nearest 32 bits */
	ui32IndexDWords = (ui32NumIndices + 1) >> 1;

	CBUF_UpdateBufferPos(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER);

	GLES2_INC_COUNT(GLES2_TIMER_INDEX_DATA_COUNT, ui32IndexDWords);

	/* Emit state */
	GLES2EmitState(gc, eMode, IMG_FALSE, ui32NumIndices, CBUF_GetBufferDeviceAddress(gc->apsBuffers, gc->pui32IndexData,
					   CBUF_TYPE_INDEX_DATA_BUFFER), 0);

}


/***********************************************************************************
 Function Name      : WriteDirect32Indices
 Inputs             : gc
                      eMode      - One of GL_POINTS, GL_LINES, GL_TRIANGLES,
                                  GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN.
                      ui32First - Position of the first index to use
                      ui32Count - Number of indices to use
                      pvIndices - Actual indices or NULL for autogenerated consecutive indices.
 Outputs            : -
 Returns            : -
 Description        : Writes 32-bit indices for point, line, triangles,
 					    triangle-strip and triangle-fan primitives into the
 					    index buffer and makes the call to emit the primitive state.
************************************************************************************/
static IMG_VOID WriteDirect32Indices(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32First,
									 IMG_UINT32 ui32Count, const IMG_VOID *pvIndices)
{
	const IMG_UINT32 *pui32Indices = (const IMG_UINT32 *)pvIndices;
	IMG_UINT32 i, * IMG_RESTRICT pui32IndicesOut;

	switch(eMode)
	{
		case GL_POINTS:
		{
			if (ui32Count<1)
			{
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
				return;
			}

			break;
		}
		case GL_LINES:
		{
			/* Make it multiple of 2 */
			ui32Count = ui32Count - ui32Count%2;

			if (ui32Count<2)
			{
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
				return;
			}

			break;
		}
		case GL_TRIANGLES:
		{
			/* Make it multiple of 3 and fall through */
			ui32Count = ui32Count - ui32Count%3;
		}
		case GL_TRIANGLE_STRIP:
		case GL_TRIANGLE_FAN:
		{
			if (ui32Count<3)
			{
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
				return;
			}

			break;
		}
		default :
		{
			/* Can't handle GL_LINE_STRIP or GL_LINE_LOOP in this function */
			PVR_DPF((PVR_DBG_WARNING,"WriteDirect32Indices: Bad eMode (0x%X) - shouldn't be here", eMode));
			return;
		}
	}

	pui32IndicesOut = gc->pui32IndexData;
	
	GLES2_TIME_START(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

	if (pui32Indices)
	{
		GLES2MemCopy(pui32IndicesOut, &pui32Indices[ui32First], sizeof(IMG_UINT32)*ui32Count);
	}
	else
	{
		for(i=ui32First; i < ui32Count + ui32First; ++i)
		{
			*pui32IndicesOut++ = i;
		}
	}

	GLES2_TIME_STOP(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

	CBUF_UpdateBufferPos(gc->apsBuffers, ui32Count, CBUF_TYPE_INDEX_DATA_BUFFER);

	GLES2_INC_COUNT(GLES2_TIMER_INDEX_DATA_COUNT, ui32Count);

	/* Emit state */
	GLES2EmitState(gc, eMode, IMG_TRUE, ui32Count,
	               CBUF_GetBufferDeviceAddress(gc->apsBuffers, gc->pui32IndexData, CBUF_TYPE_INDEX_DATA_BUFFER), 0);

}


/***********************************************************************************
 Function Name      : WriteLineStripOrLoop32Indices
 Inputs             : gc
                      eMode      - GL_LINE_STRIP or GL_LINE_LOOP
                      ui32First - Position of the first index to use
                      ui32Count - Number of indices to use
                      pvIndices - Actual indices or NULL for autogenerated consecutive indices.
 Outputs            : 
 Returns            : 
 Description        : Writes 32-bit indices for line strip/loop primitives into the index buffer
					    and makes the call to emit the primitive state.
************************************************************************************/
static IMG_VOID WriteLineStripOrLoop32Indices(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32First,
									   IMG_UINT32 ui32Count, const IMG_VOID *pvIndices)
{
	const IMG_UINT32 *pui32Indices = (const IMG_UINT32 *)pvIndices;
	IMG_UINT32 * IMG_RESTRICT pui32IndicesOut;
	IMG_UINT32 i, ui32NumIndices, i0, i1;

	GLES_ASSERT( (eMode == GL_LINE_STRIP) || (eMode == GL_LINE_LOOP) );

	if (ui32Count<2)
	{
		return;
	}

	pui32IndicesOut = gc->pui32IndexData;
	
	GLES2_TIME_START(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

	if (pui32Indices)
	{
		for (i=ui32First; i < ui32Count + ui32First - 1; pui32IndicesOut += 2)
		{
			i0 = pui32Indices[i++];
			i1 = pui32Indices[i];

			pui32IndicesOut[0] = i0;
			pui32IndicesOut[1] = i1;
		}

		i0 = pui32Indices[i];
		i1 = pui32Indices[ui32First];
	}
	else
	{
		for (i=ui32First; i < ui32Count + ui32First - 1; pui32IndicesOut += 2)
		{
			i0 = i++;
			i1 = i;

			pui32IndicesOut[0] = i0;
			pui32IndicesOut[1] = i1;
		}

		i0 = i;
		i1 = ui32First;
	}
	
	if((eMode == GL_LINE_LOOP) && (i0 != i1))
	{
		/* Add the connection from the last to the first vertex to close the loop */
		pui32IndicesOut[0] = i0;
		pui32IndicesOut[1] = i1;
		pui32IndicesOut += 2;
	}
	
	GLES2_TIME_STOP(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

	ui32NumIndices = (IMG_UINT32)(pui32IndicesOut - gc->pui32IndexData);

	CBUF_UpdateBufferPos(gc->apsBuffers, ui32NumIndices, CBUF_TYPE_INDEX_DATA_BUFFER);

	GLES2_INC_COUNT(GLES2_TIMER_INDEX_DATA_COUNT, ui32NumIndices);

	/* Emit state */
	GLES2EmitState(gc, eMode, IMG_TRUE, ui32NumIndices,
	               CBUF_GetBufferDeviceAddress(gc->apsBuffers, gc->pui32IndexData, CBUF_TYPE_INDEX_DATA_BUFFER), 0);

}



static IMG_VOID (* const WriteIndices[2][7])(GLES2Context *gc, GLenum mode, IMG_UINT32 ui32First, IMG_UINT32 ui32Count, const IMG_VOID *puiIndices) = 
{
	{
		WriteDirect16Indices,          /* GL_POINTS         */
		WriteDirect16Indices,          /* GL_LINES          */
		WriteLineStripOrLoop16Indices, /* GL_LINE_LOOP      */
		WriteLineStripOrLoop16Indices, /* GL_LINE_STRIP     */
		WriteDirect16Indices,          /* GL_TRIANGLES      */
		WriteDirect16Indices,          /* GL_TRIANGLE_STRIP */
		WriteDirect16Indices,          /* GL_TRIANGLE_FAN   */
	},
	{
		WriteDirect32Indices,          /* GL_POINTS         */
		WriteDirect32Indices,          /* GL_LINES          */
		WriteLineStripOrLoop32Indices, /* GL_LINE_LOOP      */
		WriteLineStripOrLoop32Indices, /* GL_LINE_STRIP     */
		WriteDirect32Indices,          /* GL_TRIANGLES      */
		WriteDirect32Indices,          /* GL_TRIANGLE_STRIP */
		WriteDirect32Indices,          /* GL_TRIANGLE_FAN   */
	}
};


/***********************************************************************************
 Function Name      : GetNumIndices
 Inputs             : eMode, ui32Count
 Outputs            : Number of indices required
 Returns            : 
 Description        : Returns the number of indices needed to draw the primitive
					    type 'eMode' with original number of vertices 'ui32Count'
************************************************************************************/
static IMG_UINT32 GetNumIndices(GLenum eMode, IMG_UINT32 ui32Count)
{
	GLuint ui32NumIndices;

	switch(eMode)
	{
		case GL_POINTS:
		{
			ui32NumIndices = ui32Count;

			break;
		}
		case GL_LINES:
		{
			ui32Count = ui32Count - ui32Count%2;

			ui32NumIndices = (ui32Count < 2) ? 0 : (ui32Count);

			break;
		}
		case  GL_LINE_LOOP:
		{
			ui32NumIndices = (ui32Count < 2) ? 0 : (2 * ui32Count);

			break;
		}
		case GL_LINE_STRIP:
		{
			ui32NumIndices = (ui32Count < 2) ? 0 : (2 * (ui32Count-1));

			break;
		}
		case GL_TRIANGLES:
		{
			ui32Count = ui32Count - ui32Count%3;

			/* Fall through */
		}
		case GL_TRIANGLE_STRIP:
		case GL_TRIANGLE_FAN:
		{
			ui32NumIndices = (ui32Count < 3) ? 0 : ui32Count;

			break;
		}
		default:
		{
			ui32NumIndices = 0;

			PVR_DPF((PVR_DBG_ERROR,"GetNumIndices: Bad mode (0x%X) - shouldn't be here", eMode));

			break;
		}
	}

	return ui32NumIndices;
}




/***********************************************************************************
 Function Name      : SetupStreamPointers
 Inputs             : gc, ui32First, ui32Count, bAreIndicesZeroBased
 Outputs            : -
 Returns            : -
 Description        : Set up the source, destination and PDS source pointers for vertex attribute copying.
************************************************************************************/
static IMG_VOID SetupStreamPointers(GLES2Context *gc, IMG_UINT32 ui32First,
									IMG_UINT32 ui32Count, IMG_BOOL bAreIndicesZeroBased)
{
	IMG_UINT32 i;
	IMG_UINT8  *pui8VertexData = (IMG_UINT8 *)gc->pvVertexData;
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);

	for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
	{
		GLES2AttribArrayPointerMachine *psAPMachine = psVAOMachine->apsPackedAttrib[i];
		IMG_BOOL bIsBufferObject, bIsCurrentState;
		IMG_VOID *pvOldPDSSrcAddress;

		/* Assert psAPMachine exists */
		GLES_ASSERT( psAPMachine );
			
		/* Keep a copy of the old PDS source address */
		pvOldPDSSrcAddress = psAPMachine->pvPDSSrcAddress;

		bIsCurrentState = psAPMachine->bIsCurrentState;
			
		/* When determining whether an attribute comes from a buffer object or not,
		   psAPMachine->bIsCurrentState takes precedence over whether psAPMachine->psState->psBufObj is null. */
		bIsBufferObject = (!bIsCurrentState && psAPMachine->psState->psBufObj) ? IMG_TRUE : IMG_FALSE;
		
		if(bIsBufferObject)
		{
			/* Neither pui8SrcPointer nor pui8DstPointer need to be set up */
			/* The PDS source 'pointer' is actually an offset. See WritePDSVertexShaderProgram() */
			if(bAreIndicesZeroBased)
			{
				psAPMachine->pvPDSSrcAddress = psAPMachine->pui8CopyPointer + ui32First * psAPMachine->ui32CopyStride;
			}
			else
			{
				psAPMachine->pvPDSSrcAddress = psAPMachine->pui8CopyPointer;
			}

		}
		else
		{
			IMG_UINT32 ui32StreamOffset;

			if(bIsCurrentState)
			{
				/* The source pointer isn't shifted by any offset */
				psAPMachine->pui8SrcPointer = psAPMachine->pui8CopyPointer;
				ui32StreamOffset = psAPMachine->ui32DstSize;
			}
			else
			{
			    /* The following setup is only for the default VAO */
			    if (VAO_IS_ZERO(gc))
				{
					/* The source pointer is shifted by the source offset */
					psAPMachine->pui8SrcPointer = psAPMachine->pui8CopyPointer + ui32First * psAPMachine->ui32CopyStride;
					ui32StreamOffset = psAPMachine->ui32DstSize * ui32Count;
				}
				else
				{
					ui32StreamOffset = 0;
				}
			}

			/* The destination pointer is always zero-based */
			psAPMachine->pui8DstPointer = pui8VertexData;

			/* The PDS source pointer is shifted by the value of the minimum index */
			if(bAreIndicesZeroBased || bIsCurrentState)
			{
				psAPMachine->pvPDSSrcAddress = (IMG_VOID *)(pui8VertexData);
			}
			else
			{
				psAPMachine->pvPDSSrcAddress = (IMG_VOID *)(pui8VertexData - ui32First * psAPMachine->ui32DstSize);
			}

			pui8VertexData += ui32StreamOffset;
		}

		/* If we are changing PDS stream base addresses, we need to set the dirty flag to resend PDS program */
		if(pvOldPDSSrcAddress != psAPMachine->pvPDSSrcAddress)
		{
			gc->ui32DirtyState |= GLES2_DIRTYFLAG_VAO_ATTRIB_POINTER;
		}
	}
}


/***********************************************************************************
 Function Name      : CopyVArrayData
 Inputs             : gc, ui32First, ui32Count, bAreIndicesZeroBased
 Outputs            : 
 Returns            : 
 Description        : 
************************************************************************************/
static IMG_VOID CopyVArrayData(GLES2Context *gc, IMG_UINT32 ui32First,
							   IMG_UINT32 ui32Count, IMG_BOOL bAreIndicesZeroBased)
{
	IMG_UINT32 i;
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES2AttribArrayPointerMachine *psAPMachine;
	IMG_UINT32 ui32DWordsWritten = (((gc->ui32VertexSize * ui32Count) + gc->ui32VertexRCSize + gc->ui32VertexAlignSize) + 3) >> 2;
#if defined(NO_UNALIGNED_ACCESS)
	IMG_UINT32 alignment;
#endif

	SetupStreamPointers(gc, ui32First, ui32Count, bAreIndicesZeroBased);

	GLES2_TIME_START(GLES2_TIMER_VERTEX_DATA_COPY);

	for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
	{
	    /* Retrieve APMachine from the current VAO */
	    psAPMachine = psVAOMachine->apsPackedAttrib[i];

		/* Assert psAPMachine exists */
		GLES_ASSERT( psAPMachine );

		if(psAPMachine->bIsCurrentState || !psAPMachine->psState->psBufObj)
		{
			GLES_ASSERT(psAPMachine->pui8SrcPointer);

#if defined(NO_UNALIGNED_ACCESS)
 			alignment = (((IMG_UINT32) psAPMachine->pui8SrcPointer) & 3) | (((IMG_UINT32) psAPMachine->ui32CopyStride) & 3);

 			if(psAPMachine->bIsCurrentState)
			{
				psAPMachine->pfnCopyData[alignment](psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, 1);
			}
			else
			{
				psAPMachine->pfnCopyData[alignment](psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, ui32Count);
			}
#else
			if(psAPMachine->bIsCurrentState)
			{
				psAPMachine->pfnCopyData(psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, 1);
			}
			else
			{
				psAPMachine->pfnCopyData(psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, ui32Count);
			}
#endif
		}
	}
	
	GLES2_TIME_STOP(GLES2_TIMER_VERTEX_DATA_COPY);

	CBUF_UpdateBufferPos(gc->apsBuffers, ui32DWordsWritten, CBUF_TYPE_VERTEX_DATA_BUFFER);

	GLES2_INC_COUNT(GLES2_TIMER_VERTEX_DATA_COUNT, ui32DWordsWritten);
}


/***********************************************************************************
 Function Name      : CopyVArrayDataDeindex
 Inputs             : gc, ui32First, ui32Count, pui32Elements, bCopyBOData
 Outputs            : 
 Returns            : 
 Description        : Copies vertex attributes to the vertex buffer deindexing (gathering) them.
                      If the source of an attribute is a buffer object, the vertices are
                      also copied to the vertex buffer.
************************************************************************************/
static IMG_VOID CopyVArrayDataDeindex(GLES2Context *gc, IMG_UINT32 ui32First,
									  IMG_UINT32 ui32Count, const IMG_VOID *pvElements,
									  IMG_BOOL bAreElements32Bit)
{
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES2AttribArrayPointerMachine *psAPMachine;
	IMG_UINT32 i;
	IMG_UINT32 ui32DWordsWritten = (((gc->ui32VertexSize * ui32Count) + gc->ui32VertexRCSize + gc->ui32VertexAlignSize) + 3) >> 2;
#if defined(NO_UNALIGNED_ACCESS)
	IMG_UINT32 alignment;
#endif

	GLES_ASSERT(pvElements);

	SetupStreamPointers(gc, ui32First, ui32Count, IMG_TRUE);

	GLES2_TIME_START(GLES2_TIMER_VERTEX_DATA_COPY);

	for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
	{
	    /* Retrieve APMachine from the current VAO */
	    psAPMachine = psVAOMachine->apsPackedAttrib[i];

		/* Assert psAPMachine exists */
		GLES_ASSERT( psAPMachine );
		GLES_ASSERT( psAPMachine->pui8SrcPointer );
		
		if(psAPMachine->bIsCurrentState)
		{
#if defined(NO_UNALIGNED_ACCESS)
			alignment = (((IMG_UINT32) psAPMachine->pui8SrcPointer) & 3) | (((IMG_UINT32) psAPMachine->ui32CopyStride) & 3);
			psAPMachine->pfnCopyData[alignment](psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, 1);
#else
			psAPMachine->pfnCopyData(psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, 1);
#endif
		}
		else
		{
			IMG_UINT8 *pui8DstPointer;
			IMG_UINT8 *pui8SrcPointer, *pui8SrcBasePointer;
			IMG_UINT32 j;

			/* pui8SrcBasePointer points to the base of the vertex data */
			pui8SrcBasePointer = psAPMachine->pui8SrcPointer - ui32First * psAPMachine->ui32Stride;
			pui8DstPointer =  psAPMachine->pui8DstPointer;

			/* The two branches only differ in the type of the elements: 16-bit or 32-bit unsigned ints */
			if(bAreElements32Bit)
			{
				const IMG_UINT32 *pui32Elements = (const IMG_UINT32*)pvElements;

				for(j=0; j < ui32Count; j++)
				{
					pui8SrcPointer = pui8SrcBasePointer + pui32Elements[ui32First+j] * psAPMachine->ui32Stride;

#if defined(NO_UNALIGNED_ACCESS)
					alignment = (((IMG_UINT32) pui8SrcPointer) & 3) | (((IMG_UINT32) psAPMachine->ui32CopyStride) & 3);
					psAPMachine->pfnCopyData[alignment](pui8SrcPointer, pui8DstPointer, psAPMachine->ui32CopyStride, 1);
#else
					psAPMachine->pfnCopyData(pui8SrcPointer, pui8DstPointer, psAPMachine->ui32CopyStride, 1);
#endif

					pui8DstPointer += psAPMachine->ui32DstSize;
				}
			}
			else
			{
				const IMG_UINT16 *pui16Elements = (const IMG_UINT16*)pvElements;

				for(j=0; j < ui32Count; j++)
				{
					pui8SrcPointer = pui8SrcBasePointer + pui16Elements[ui32First+j] * psAPMachine->ui32Stride;

#if defined(NO_UNALIGNED_ACCESS)
					alignment = (((IMG_UINT32) pui8SrcPointer) & 3) | (((IMG_UINT32) psAPMachine->ui32CopyStride) & 3);
					psAPMachine->pfnCopyData[alignment](pui8SrcPointer, pui8DstPointer, psAPMachine->ui32CopyStride, 1);
#else
					psAPMachine->pfnCopyData(pui8SrcPointer, pui8DstPointer, psAPMachine->ui32CopyStride, 1);
#endif

					pui8DstPointer += psAPMachine->ui32DstSize;
				}
			}
		}
	}
	
	GLES2_TIME_STOP(GLES2_TIMER_VERTEX_DATA_COPY);

	CBUF_UpdateBufferPos(gc->apsBuffers, ui32DWordsWritten, CBUF_TYPE_VERTEX_DATA_BUFFER);

	GLES2_INC_COUNT(GLES2_TIMER_VERTEX_DATA_COUNT, ui32DWordsWritten);
}


/***********************************************************************************
 Function Name      : GetVertexIndexBufferSpace
 Inputs             : gc, uNumIndices, uNumVertices, uSizePerIndex
 Outputs            : 
 Returns            : 
 Description        : check the vertex and index buffer space, flush when necessary 
************************************************************************************/
static IMG_VOID GetVertexIndexBufferSpace(GLES2Context *gc, IMG_UINT32 ui32NumIndices,
											IMG_UINT32 ui32NumVertices, IMG_UINT32 ui32SizePerIndex)
{
	/* Round byte calc up to nearest dword */
	IMG_UINT32 ui32VertexDWords = (((ui32NumVertices * gc->ui32VertexSize) + gc->ui32VertexRCSize + gc->ui32VertexAlignSize) + 3) >> 2;
	IMG_UINT32 ui32IndexDWords = ((ui32NumIndices * ui32SizePerIndex) + 3) >> 2;
	IMG_BOOL bKickTA = IMG_FALSE;

	/* An overflow render could take us out of frame, so check here */
	if(!gc->psRenderSurface->bInFrame)
	{
		IMG_UINT32 ui32NoClears=0;
		IMG_BOOL bSuccess;

		bSuccess = PrepareToDraw(gc, &ui32NoClears, IMG_FALSE);

		if(!bSuccess)
		{
			PVR_DPF((PVR_DBG_ERROR,"GetVertexIndexBufferSpace: Can't prepare to draw"));
		}

		GLES_ASSERT(bSuccess);
	}

	gc->pvVertexData = (IMG_VOID *) CBUF_GetBufferSpace(gc->apsBuffers, ui32VertexDWords,
												CBUF_TYPE_VERTEX_DATA_BUFFER, IMG_FALSE);
		
	if(!gc->pvVertexData)
	{
		if(gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32CommittedPrimOffsetInBytes != 
			gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32CommittedHWOffsetInBytes)
		{
			bKickTA = IMG_TRUE;

#if defined(DEBUG) || defined(TIMING)
			gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32KickCount++;
#endif /* defined(DEBUG) || defined(TIMING) */
		}
	}

	/* No need to get index buffer space now if we are about to kick the TA */
	if(!bKickTA)
	{
		gc->pui32IndexData = CBUF_GetBufferSpace(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);

		if(!gc->pui32IndexData)
		{
			if(gc->apsBuffers[CBUF_TYPE_INDEX_DATA_BUFFER]->ui32CommittedPrimOffsetInBytes != 
				gc->apsBuffers[CBUF_TYPE_INDEX_DATA_BUFFER]->ui32CommittedHWOffsetInBytes)
			{
				bKickTA = IMG_TRUE;

				/* Unlock the vertex buffer space, as we're about to kick */
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_VERTEX_DATA_BUFFER);

#if defined(DEBUG) || defined(TIMING)
				gc->apsBuffers[CBUF_TYPE_INDEX_DATA_BUFFER]->ui32KickCount++;
#endif /* defined(DEBUG) || defined(TIMING) */
			}
		}
	}

	if(bKickTA)
	{
		ScheduleTA(gc, gc->psRenderSurface, 0);

		GLES_ASSERT(gc->psRenderSurface->bInFrame);

		gc->pvVertexData = (IMG_VOID *) CBUF_GetBufferSpace(gc->apsBuffers, ui32VertexDWords,
												CBUF_TYPE_VERTEX_DATA_BUFFER, IMG_FALSE);

		gc->pui32IndexData = CBUF_GetBufferSpace(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);
	}

	GLES_ASSERT(gc->pvVertexData);
	GLES_ASSERT(gc->pui32IndexData);
}


/***********************************************************************************
 Function Name      : CreateSequentialStaticIndices
 Inputs             : gc
 Outputs            : -
 Returns            : Whether static buffer is useable or not
 Description        : Creates a static buffer filled with incremental indices
************************************************************************************/
static IMG_BOOL CreateSequentialStaticIndices(GLES2Context *gc)
{
	IMG_UINT16 i;
	IMG_UINT16 *pui16Index;

	PVRSRVLockMutex(gc->psSharedState->hPrimaryLock);

	if(!gc->psSharedState->psSequentialStaticIndicesMemInfo)
	{
		if(GLES2ALLOCDEVICEMEM(gc->ps3DDevData,
							   gc->psSysContext->hGeneralHeap,
							   PVRSRV_MEM_READ,
							   (GLES2_NUM_STATIC_INDICES*sizeof(IMG_UINT16)),
							   4,
							   &gc->psSharedState->psSequentialStaticIndicesMemInfo) != PVRSRV_OK)
		{
			PVR_DPF((PVR_DBG_ERROR,"CreateSequentialStaticIndices: Can't allocate memory for static index buffer"));

			gc->psSharedState->psSequentialStaticIndicesMemInfo = IMG_NULL;

			PVRSRVUnlockMutex(gc->psSharedState->hPrimaryLock);

			return IMG_FALSE;
		}

#ifdef PDUMP
		gc->psSharedState->bMustDumpSequentialStaticIndices = IMG_TRUE;
#endif

		pui16Index = (IMG_UINT16 *)gc->psSharedState->psSequentialStaticIndicesMemInfo->pvLinAddr;

		for(i=0; i<GLES2_NUM_STATIC_INDICES; i++)
		{
			pui16Index[i] = i;
		}
	}

	PVRSRVUnlockMutex(gc->psSharedState->hPrimaryLock);

	return IMG_TRUE;
}	


/***********************************************************************************
 Function Name      : CreateLineStripStaticIndices
 Inputs             : gc
 Outputs            : -
 Returns            : Whether static buffer is useable or not
 Description        : Creates a static buffer filled with indices for a line strip
************************************************************************************/
static IMG_BOOL CreateLineStripStaticIndices(GLES2Context *gc)
{
	IMG_UINT16 i;
	IMG_UINT16 *pui16Index;

	PVRSRVLockMutex(gc->psSharedState->hPrimaryLock);

	if(!gc->psSharedState->psLineStripStaticIndicesMemInfo)
	{
		if(GLES2ALLOCDEVICEMEM(gc->ps3DDevData,
							   gc->psSysContext->hGeneralHeap,
							   PVRSRV_MEM_READ,
							   (GLES2_NUM_STATIC_INDICES*sizeof(IMG_UINT16)),
							   4,
							   &gc->psSharedState->psLineStripStaticIndicesMemInfo) != PVRSRV_OK)
		{
			PVR_DPF((PVR_DBG_ERROR,"CreateLineStripStaticIndices: Can't allocate memory for static index buffer"));

			gc->psSharedState->psLineStripStaticIndicesMemInfo = IMG_NULL;

			PVRSRVUnlockMutex(gc->psSharedState->hPrimaryLock);

			return IMG_FALSE;
		}

#ifdef PDUMP
		gc->psSharedState->bMustDumpLineStripStaticIndices = IMG_TRUE;
#endif

		pui16Index = (IMG_UINT16 *)gc->psSharedState->psLineStripStaticIndicesMemInfo->pvLinAddr;

		for(i=0; i<GLES2_NUM_STATIC_INDICES/2; i++)
		{
			pui16Index[(2*i)] = i;
			pui16Index[(2*i)+1] = i+1;
		}
	}

	PVRSRVUnlockMutex(gc->psSharedState->hPrimaryLock);

	return IMG_TRUE;
}	


/***********************************************************************************
 Function Name      : GetVArrayBatchRewindCount
 Inputs             : eMode, uBatchSize
 Outputs            : uBatchSize
 Returns            : uRewindCount
 Description        : modify batch size and get the rewind count according to eMode
************************************************************************************/
static IMG_UINT32 GetVArrayBatchRewindCount(GLenum eMode, IMG_UINT32 *ui32BatchSize)
{
	GLuint ui32BatchCount = *ui32BatchSize;
	GLuint ui32RewindCount = 0;

	switch(eMode)
	{
		case GL_TRIANGLES:
		{
			ui32BatchCount -= ui32BatchCount%3;

			break;
		}
		case GL_LINES:
		{
			ui32BatchCount -= ui32BatchCount%2;

			break;
		}
		case GL_TRIANGLE_STRIP:
		{
			ui32BatchCount -= ui32BatchCount%2;

			ui32RewindCount = 2;

			break;
		}
		case GL_LINE_STRIP:
		case GL_LINE_LOOP:
		{
			ui32RewindCount = 1;

			break;
		}
		case GL_TRIANGLE_FAN:
		{
			ui32RewindCount = 2;
		}
		default:
		{
			break;
		}
	}

	*ui32BatchSize = ui32BatchCount;

	return ui32RewindCount;
}


/***********************************************************************************
 Function Name      : DrawVertexArray
 Inputs             : gc
                      eMode - From GL_POINTS to GL_TRIANGLE_FAN
                      ui32First - Position of the first index to read
                      ui32Count - Number of indices to read
                      eType  - Must be GL_UNSIGNED_SHORT
                      elements - Must be NULL or an array of IMG_UINT16 with at least ui32First + ui32Count elements.
                      ui32VertexStart
                      ui32VertexCount
 Outputs            : 
 Returns            : 
 Description        : Implementation of DrawArrays/DrawElements. All vertex
                      and index data must fit in their respective buffers. Writes 16-bit indices
************************************************************************************/
static IMG_VOID DrawVertexArray(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32Count,
								IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
								IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
	IMG_BOOL   bIndicesAreZeroBased = (elements==IMG_NULL)?IMG_TRUE:IMG_FALSE;

	GLES_ASSERT(!elements || eType != GL_UNSIGNED_BYTE);

	if (bIndicesAreZeroBased && eMode==GL_LINE_STRIP &&
		ui32NumIndices<=GLES2_NUM_STATIC_INDICES && CreateLineStripStaticIndices(gc))
	{
		/* Check the vertex buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		/* Emit state */
		GLES2EmitState(gc, eMode, IMG_FALSE, ui32NumIndices, 
					   gc->psSharedState->psLineStripStaticIndicesMemInfo->sDevVAddr, 0);
	}
	else
	{
		IMG_UINT32 ui32Write32BitIndices = 0;

		if(elements && (eType == GL_UNSIGNED_INT))
		{
			ui32Write32BitIndices = 1;
		}
		
		/* Check the vertex and index buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, ui32NumIndices, ui32VertexCount, ui32Write32BitIndices?sizeof(IMG_UINT32):sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, bIndicesAreZeroBased);

		/* Write indices, may be 16 or 32-bit */
		(WriteIndices[ui32Write32BitIndices][eMode])(gc, eMode, bIndicesAreZeroBased?0:ui32First, ui32Count, elements);
	}
}


/***********************************************************************************
 Function Name      : DrawArraysBatchedStaticIndices
 Inputs             : gc, eMode, ui32First, ui32Count, ui32NumIndices, eType, 
 					  elements,	ui32VertexStart, ui32VertexCount
 Outputs            : -
 Returns            : -
 Description        : Send batches of static indices for points, lines, triangles,
					  and triangle strips
************************************************************************************/
static IMG_VOID DrawArraysBatchedStaticIndices(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32Count,
									IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
									IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
	IMG_UINT32 ui32IndexOffset, ui32RewindCount, ui32BatchSize;

	/* This function autogenerates the indices */
	GLES_ASSERT(!elements);
	PVR_UNREFERENCED_PARAMETER(elements);
	PVR_UNREFERENCED_PARAMETER(ui32First);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32Count);
	PVR_UNREFERENCED_PARAMETER(eMode);

	/* Check the vertex buffer space, flush when necessary */
	GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, sizeof(IMG_UINT16));

	/* Setup stream addresses and copy the vertices attributes if required */
	CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

	CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

	ui32IndexOffset = 0;

	ui32RewindCount = GetVArrayBatchRewindCount(eMode, &ui32BatchSize);

	do
	{
		ui32BatchSize = MIN((ui32NumIndices-ui32IndexOffset), GLES2_NUM_STATIC_INDICES);

		ui32BatchSize = GetNumIndices(eMode, ui32BatchSize);

		/* Emit state */
		GLES2EmitState(gc, eMode, IMG_FALSE, ui32BatchSize, 
					   gc->psSharedState->psSequentialStaticIndicesMemInfo->sDevVAddr, ui32IndexOffset);

		ui32IndexOffset += ui32BatchSize - ui32RewindCount;

	} while(ui32IndexOffset + ui32RewindCount < ui32NumIndices);
}


/***********************************************************************************
 Function Name      : DrawArraysAutoIndices
 Inputs             : gc
                      eMode - From GL_POINTS to GL_TRIANGLE_FAN
                      ui32First - Position of the first index to read
                      ui32Count - Number of indices to read
                      eType  - Ignored.
                      elements - Must be NULL.
                      ui32VertexStart
                      ui32VertexCount
 Outputs            : 
 Returns            : 
 Description        : Auto generate consecutive index data for DrawArrays. Writes 16-bit indices.
************************************************************************************/
static IMG_VOID DrawArraysAutoIndices(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32Count,
									IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
									IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
	/* This function autogenerates the indices */
	GLES_ASSERT(!elements);
	PVR_UNREFERENCED_PARAMETER(ui32Count);
	PVR_UNREFERENCED_PARAMETER(elements);
	PVR_UNREFERENCED_PARAMETER(ui32First);
	PVR_UNREFERENCED_PARAMETER(eType);

	/* The type must be natively supported by the hardware */
	GLES_ASSERT(primDirectIndex[eType]);

	if(ui32NumIndices<=GLES2_NUM_STATIC_INDICES && CreateSequentialStaticIndices(gc))
	{
		/* Check the vertex buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		/* Emit state */
		GLES2EmitState(gc, eMode, IMG_FALSE, ui32NumIndices, 
					   gc->psSharedState->psSequentialStaticIndicesMemInfo->sDevVAddr, 0);
	}
	else
	{
		IMG_UINT16 *pui16Indices;
		IMG_UINT32 ui32IndexDWords, i;

		/* Check the vertex and index buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, ui32NumIndices, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		/* Write 16-bit indices */
		pui16Indices = (IMG_UINT16 *)gc->pui32IndexData;

		GLES2_TIME_START(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

		for(i=0; i < ui32NumIndices; ++i)
		{
			pui16Indices[i] = (IMG_UINT16)i;
		}
		
		GLES2_TIME_STOP(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

		/* Round up to nearest 32 bits */
		ui32IndexDWords = (ui32NumIndices + 1) >> 1;

		CBUF_UpdateBufferPos(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER);

		GLES2_INC_COUNT(GLES2_TIMER_INDEX_DATA_COUNT, ui32IndexDWords);

		/* Emit state */
		GLES2EmitState(gc, eMode, IMG_FALSE, ui32NumIndices,
		               CBUF_GetBufferDeviceAddress(gc->apsBuffers, gc->pui32IndexData, CBUF_TYPE_INDEX_DATA_BUFFER), 0);
	}
}


/***********************************************************************************
 Function Name      : DrawElementsDeindex
 Inputs             : gc
                      eMode - From GL_POINTS to GL_TRIANGLE_FAN
                      ui32First - Position of the first index to read
                      ui32Count - Number of indices to read
                      eType  - Ignored.
                      elements - Must be NULL.
                      ui32VertexStart
                      ui32VertexCount
 Outputs            : 
 Returns            : 
 Description        : De-indexing DrawElements function. All vertex and index data  
                      must fit in their respective buffers. Writes 16-bit indices.
************************************************************************************/
static IMG_VOID DrawElementsDeindex(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32Count,
									IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
									IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
	IMG_BOOL b32BitIndices = (eType==GL_UNSIGNED_INT) ? IMG_TRUE : IMG_FALSE;

	GLES_ASSERT(elements);

	PVR_UNREFERENCED_PARAMETER(ui32First);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32VertexStart);
	PVR_UNREFERENCED_PARAMETER(ui32VertexCount);

	/* We should not deindex BOs unless v and i data cannot fit in */
	GLES_ASSERT(!(gc->sVAOMachine.ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ));

	/* Check the vertex and index buffer space, flush when necessary */
	GetVertexIndexBufferSpace(gc, ui32NumIndices, ui32Count, sizeof(IMG_UINT16));

	/* Setup stream addresses and copy the vertices attributes if required */
	CopyVArrayDataDeindex(gc, 0, ui32Count, elements, b32BitIndices);

	/* Write 16-bit indices */
	(WriteIndices[0][eMode])(gc, eMode, 0, ui32Count, IMG_NULL);

}

/***********************************************************************************
 Function Name      : DrawElementsIndexBO
 Inputs             : gc
                      eMode - From GL_POINTS to GL_TRIANGLE_FAN
                      ui32First - Position of the first index to read
                      ui32Count - Number of indices to read
                      eType  - One of GL_UNSIGNED_SHORT or GL_UNSIGNED_INT
                      elements - Must be an array of IMG_UINT16/32 with at least ui32First + ui32Count elements.
                      ui32VertexStart
                      ui32VertexCount
 Outputs            : 
 Returns            : 
 Description        : The indices come from an index buffer object. All vertex 
                      data should fit in the vertex buffer.
************************************************************************************/
static IMG_VOID DrawElementsIndexBO(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32UnusedCount,
									IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
									IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
    PVRSRV_CLIENT_MEM_INFO *psMemInfo;
	IMG_UINT32 ui32Offset = (IMG_UINT32)GLES2_BUFFER_OFFSET(elements);
	IMG_DEV_VIRTADDR uIndexAddress;
	IMG_UINT32 ui32BatchSize, ui32RewindCount, ui32Count, ui32IndexStart;
	IMG_UINT32 ui32IndexSize;
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES2BufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;


	PVR_UNREFERENCED_PARAMETER(ui32First);
	PVR_UNREFERENCED_PARAMETER(ui32UnusedCount);


	/* Setup psMemInfo using VAOMachine's bound element bufobj */
	GLES_ASSERT(psIndexBO);

	psMemInfo = psIndexBO->psMemInfo;

	GLES_ASSERT(psMemInfo);


	/* Check the vertex and index buffer space, flush when necessary */
	GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, 0);

	/* Setup stream addresses and copy the vertices attributes if required */
	CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_FALSE);
	
	ui32IndexSize = (eType==GL_UNSIGNED_INT) ? sizeof(IMG_UINT32) : sizeof(IMG_UINT16);

	uIndexAddress.uiAddr = psMemInfo->sDevVAddr.uiAddr;
	uIndexAddress.uiAddr += (IMG_UINT32)ui32Offset;
	
	if(ui32Offset > psMemInfo->uAllocSize)
	{
		PVR_DPF((PVR_DBG_ERROR,"Index offset %u is larger than index buffer size %u", ui32Offset, psMemInfo->uAllocSize));
	}
	
	CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

	/* 
	 * After the first batch has been drawn, it needs some vertices overlaped for the second batch, 
	 * the following function calculates this rewind count, at the same time, modifying uBatchSize
	 * to a proper figure.
	 */
	ui32BatchSize = MIN(ui32NumIndices, MAX_INDICES_PER_PRIMBLOCK(ui32IndexSize));	/* PRQA S 3356 */ /* Use this expression to keep code flexible as MAX_INDEX_ADDRESS may have different values. */
	if(ui32BatchSize < ui32NumIndices)
	{
		ui32RewindCount = GetVArrayBatchRewindCount(eMode, &ui32BatchSize);
	}
	else
	{
		ui32RewindCount = 0;
	}
	ui32IndexStart = 0;

	do
	{	
		ui32Count = MIN(ui32BatchSize, ui32NumIndices - ui32IndexStart);

		/* Emit state */
		GLES2EmitState(gc, eMode, (eType==GL_UNSIGNED_INT) ? IMG_TRUE : IMG_FALSE, ui32Count, uIndexAddress, 0);
		
		ui32IndexStart += ui32Count - ui32RewindCount;
		uIndexAddress.uiAddr += ((ui32Count - ui32RewindCount) * ui32IndexSize);

	} while(ui32IndexStart + ui32RewindCount < ui32NumIndices);
}




/***********************************************************************************
 Function Name      : DrawBatchOnVBuffer
 Inputs             : gc, eMode, ui32First, count, eType, elements, ui32VertexStart, ui32VertexCount
 Outputs            : 
 Returns            : 
 Description        : De-indexes the vertices and writes them out in batches.
                      Generates 16-bit indices.
************************************************************************************/
static IMG_VOID DrawBatchOnVBuffer(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32CountIn,
								   IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
								   IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
	IMG_UINT32 i;
	IMG_UINT32 ui32IndexStart = 0;
	IMG_UINT32 ui32RewindCount;
	IMG_UINT32 ui32BatchSize = ui32CountIn;
	const IMG_UINT32 *pui32Indices = (const IMG_UINT32 *)elements;
	const IMG_UINT16 *pui16Indices = (const IMG_UINT16 *)elements;
	IMG_BOOL bAreIndices32Bit = (eType == GL_UNSIGNED_INT)? IMG_TRUE:IMG_FALSE;
	GLES2AttribArrayPointerMachine *psAPMachine;
	GLES2BufferObject *apsBufObjs[AP_VERTEX_ATTRIB_MAX];
	IMG_BOOL bDeIndex = (elements)?IMG_TRUE : IMG_FALSE;
	IMG_BOOL bAttributesWereMangled = IMG_FALSE;
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);


	PVR_UNREFERENCED_PARAMETER(ui32NumIndices);
	PVR_UNREFERENCED_PARAMETER(ui32VertexStart);
	PVR_UNREFERENCED_PARAMETER(ui32VertexCount);

	/*
	 * If any of the attributes comes from a buffer object,
	 * we have to reset the stream pointers because we are going to de-index.
	 * This is our way to punish the bad bad app that forced us to go through this code path.
	 */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ)
	{
		bAttributesWereMangled = IMG_TRUE;
		/*
		 * Make a backup copy of the buffer objects pointers of the attributes.
		 * Set up the attrib pointers to force CopyArrayDataDeIndex to copy the vertex attributes from
		 * the buffer objects into the vertex buffer.
		 */
		for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			/* Get psAPMachine & Assert it exists */
		    psAPMachine = psVAOMachine->apsPackedAttrib[i];
			GLES_ASSERT( psAPMachine );
				
			apsBufObjs[i] = psAPMachine->psState->psBufObj;
				
			if(apsBufObjs[i])
			{
				psAPMachine->psState->psBufObj = 0;
			}
		}

		gc->ui32DirtyState |= GLES2_DIRTYFLAG_VAO_ATTRIB_STREAM;


		/* Validate all the states at this point */
		if(ValidateState(gc)!=GLES2_NO_ERROR)
		{
			PVR_DPF((PVR_DBG_ERROR,"DrawBatchOnVBuffer: ValidateState() failed"));
			
			goto ResetAttribsAndReturn;
		}

		for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			/* Get psAPMachine & Assert it exists */
			psAPMachine = psVAOMachine->apsPackedAttrib[i];
			GLES_ASSERT( psAPMachine );
			
			/* The value of pui8CopyPointer is actually an offset based on the buffer object,
			   so we need to add it to get a real pointer. */
			if(apsBufObjs[i] && !psAPMachine->bIsCurrentState)
			{
				psAPMachine->pui8CopyPointer += (IMG_UINT32)apsBufObjs[i]->psMemInfo->pvLinAddr;
			}
		}
	}

	/* work out the batch size */
	if(gc->ui32VertexSize)
	{
		ui32BatchSize = MIN(ui32BatchSize, (MAX_VBUFFER - gc->ui32VertexRCSize - gc->ui32VertexAlignSize) / gc->ui32VertexSize);
	}

	ui32BatchSize = MIN(ui32BatchSize, MAX_IBUFFER / (sizeof(IMG_UINT16)*primIndexMult[eMode]));
	ui32BatchSize = MIN(64*1024, ui32BatchSize); 

	/* 
	 * After the first batch has been drawn, it needs some vertices overlaped for the second batch, 
	 * the following function calculates this rewind count, at the same time, modifying uBatchSize
	 * to a proper figure.
	 */
	if(ui32BatchSize < ui32CountIn)
	{
		ui32RewindCount = GetVArrayBatchRewindCount(eMode, &ui32BatchSize);
	}
	else
	{
		ui32RewindCount = 0;
	}

	switch(eMode)
	{
		case GL_POINTS:          /* Intentional fall-through */
		case GL_TRIANGLES:
		case GL_LINES:
		case GL_TRIANGLE_STRIP:
		case GL_LINE_STRIP:
		case GL_LINE_LOOP:
		{
			IMG_UINT32 ui32Count, ui32BatchIndices;

			do
			{	
				ui32Count = MIN(ui32BatchSize, ui32CountIn - ui32IndexStart);
				ui32BatchIndices = GetNumIndices(eMode, ui32Count);

				if(ui32BatchIndices)
				{
					/*check the vertex and index buffer space, flush when necessary */
					GetVertexIndexBufferSpace(gc, GetNumIndices(eMode, ui32Count), ui32Count, sizeof(IMG_UINT16));

					if(bDeIndex)
					{
						CopyVArrayDataDeindex(gc, ui32IndexStart + ui32First, ui32Count, elements, bAreIndices32Bit);
					}
					else
					{
						CopyVArrayData(gc, ui32IndexStart + ui32First, ui32Count, IMG_TRUE);
					}

					if(GL_LINE_LOOP == eMode)
					{
						/*
						* We override the line mode because we don't want to connect the last vertex of each
						* batch to the first vertex of each bach.
						*/
						(WriteIndices[0][GL_LINE_STRIP])(gc, GL_LINE_STRIP, 0, ui32Count, IMG_NULL);
					}
					else
					{
						(WriteIndices[0][eMode])(gc, eMode, 0, ui32Count, IMG_NULL);
					}
				}
				
				ui32IndexStart += ui32Count - ui32RewindCount;

				CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

			} while(ui32IndexStart + ui32RewindCount< ui32CountIn);

			if(GL_LINE_LOOP == eMode)
			{
				/* Connect the last vertex with the first one */
				IMG_UINT32 pui32Tmpindex[2];
				IMG_UINT32 ui32PreviousIndex = (ui32RewindCount == 0) ? ui32IndexStart - 1 : ui32IndexStart;
				
				/* Check the vertex and index buffer space, flush when necessary */
				GetVertexIndexBufferSpace(gc, 2, 2, sizeof(IMG_UINT16));

				if(elements)
				{
					if(bAreIndices32Bit)
					{
						pui32Tmpindex[0] = pui32Indices[ui32PreviousIndex+ui32First];
						pui32Tmpindex[1] = pui32Indices[ui32First];
					}
					else
					{
						pui32Tmpindex[0] = pui16Indices[ui32PreviousIndex+ui32First];
						pui32Tmpindex[1] = pui16Indices[ui32First];
					}
				}
				else
				{
					pui32Tmpindex[0] = ui32PreviousIndex + ui32First;
					pui32Tmpindex[1] = ui32First;
				}

				CopyVArrayDataDeindex(gc, 0, 2, pui32Tmpindex, IMG_TRUE);

				(WriteIndices[0][GL_LINE_STRIP])(gc, GL_LINE_STRIP, 0, 2, IMG_NULL);
			}

			break;
		}
		case GL_TRIANGLE_FAN:
		{
			IMG_UINT32 * IMG_RESTRICT pui32Tmpindex;
			IMG_UINT32 ui32Count, ui32BatchIndices;

			pui32Tmpindex = GLES2Malloc(gc, ui32BatchSize * sizeof(IMG_UINT32));

			if(!pui32Tmpindex)
			{
				PVR_DPF((PVR_DBG_WARNING,"DrawBatchOnVBuffer: Out of memory for pui32Tmpindex"));
				SetError(gc, GL_OUT_OF_MEMORY);
				goto ResetAttribsAndReturn;
			}

			do
			{
				ui32Count = MIN(ui32BatchSize, ui32CountIn - ui32IndexStart);
				ui32BatchIndices = GetNumIndices(eMode, ui32Count);

				if(ui32BatchIndices)
				{
					/* Check the vertex and index buffer space, flush when necessary */
					GetVertexIndexBufferSpace(gc, ui32Count, ui32Count, sizeof(IMG_UINT16));

					/* Check that we don't overflow pui32Tmpindex */
					GLES_ASSERT(ui32Count <= ui32BatchSize);
				
					if(elements)
					{
						/* for DrawElements */
						if(bAreIndices32Bit)
						{
							const IMG_UINT32 *pui32Src = &pui32Indices[ui32IndexStart + ui32First];

							GLES2MemCopy(pui32Tmpindex, pui32Src, ui32Count*sizeof(IMG_UINT32) );

							pui32Tmpindex[0] = pui32Indices[ui32First];
						}
						else
						{
							const IMG_UINT16 *pui16Src = &pui16Indices[ui32IndexStart + ui32First];

							pui32Tmpindex[0] = pui16Indices[ui32First];

							for(i = 1; i < ui32Count; ++i)
							{
								pui32Tmpindex[i] = pui16Src[i];
							}
						}
					}
					else
					{
						/* for DrawArrays */
						pui32Tmpindex[0] = ui32First;

						for(i = 1; i < ui32Count; ++i)
						{
							pui32Tmpindex[i] = ui32IndexStart + ui32First + i;
						}
					}

					CopyVArrayDataDeindex(gc, 0, ui32Count, pui32Tmpindex, IMG_TRUE);

					(WriteIndices[0][eMode])(gc, eMode, 0, ui32Count, IMG_NULL);
				}

				ui32IndexStart += (ui32Count - ui32RewindCount);
		
				CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

			} while(ui32IndexStart + ui32RewindCount < ui32CountIn);

			GLES2Free(IMG_NULL, pui32Tmpindex);
			break;
		}
		default:
		{
			PVR_DPF((PVR_DBG_ERROR,"DrawBatchOnVBuffer: Bad mode (0x%X) - shouldn't be here", eMode));
		}
	}

ResetAttribsAndReturn:
	if(bAttributesWereMangled)
	{
		/* Restore the pointers to buffer objects and 
		   set up the dirty flags to recompute the stream pointers. */

	    for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			psVAOMachine->apsPackedAttrib[i]->psState->psBufObj = apsBufObjs[i];
		}

		/* Only set context's dirty state, no need for VAO's */
		gc->ui32DirtyState    |= GLES2_DIRTYFLAG_VAO_ATTRIB_STREAM;
	}

}


/***********************************************************************************
 Function Name      : PickDrawArraysProc
 Inputs             : gc, eMode, ui32Count
 Outputs            : 
 Returns            : 
 Description        : Pick DrawArrays process function
************************************************************************************/
static PFNDrawVArray PickDrawArraysProc(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32Count)
{
	GLuint ui32NumIndices = ui32Count;
	PFNDrawVArray drawVArray;

	IMG_BOOL bVertexFit = IMG_TRUE;
	IMG_BOOL bIndexFit = IMG_TRUE;

	if((ui32NumIndices * sizeof(IMG_UINT16)) > MAX_IBUFFER || (ui32Count > 64*1024))
	{
		bIndexFit = IMG_FALSE;
	}

	if((ui32Count * gc->ui32VertexSize + gc->ui32VertexRCSize + gc->ui32VertexAlignSize) > MAX_VBUFFER)
	{
		bVertexFit = IMG_FALSE;
	}

	if(bVertexFit && 
	   primDirectIndex[eMode] && 
	  (eMode!=GL_TRIANGLE_FAN) && 
	  (ui32Count>GLES2_NUM_STATIC_INDICES) && 
	   CreateSequentialStaticIndices(gc))
	{
		return &DrawArraysBatchedStaticIndices;
	}

	if(bVertexFit && 
		bIndexFit && 
		primDirectIndex[eMode])
	{
		drawVArray = DrawArraysAutoIndices;

		return drawVArray;
	}

	/* modified the indices needed, recheck the index buffer */
	ui32NumIndices = GetNumIndices(eMode, ui32Count);

	bIndexFit = IMG_TRUE;

	if((ui32NumIndices * sizeof(IMG_UINT16)) > MAX_IBUFFER) 
	{
		bIndexFit = IMG_FALSE;
	}

	if(bVertexFit && bIndexFit)
	{
		drawVArray = DrawVertexArray;
	}
#if 0
	else if(bVertexFit)
	{
		drawVArray = DrawBatchOnIBuffer;
	}
#endif
	else
	{
		drawVArray = DrawBatchOnVBuffer;
	}

	return drawVArray;
}


/***********************************************************************************
 Function Name      : PickDrawElementsProc
 Inputs             : gc, eMode, eType, ui32Count, ui32VertexCount, ui32MaxIndex
 Outputs            : 
 Returns            : 
 Description        : Pick DrawElement process function
************************************************************************************/
static PFNDrawVArray PickDrawElementsProc(GLES2Context *gc, GLenum eMode, GLenum eType,
										  IMG_UINT32 ui32Count, IMG_UINT32 ui32VertexCount, IMG_UINT32 ui32MaxIndex)
{
	IMG_UINT32 ui32NumIndices = ui32Count;
	IMG_UINT32 ui32SizePerIndex = (eType == GL_UNSIGNED_INT) ? sizeof(IMG_UINT32) : sizeof(IMG_UINT16);
	PFNDrawVArray drawVArray;
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES2BufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;

	IMG_BOOL bIndexBO,
	         bSourceBO,
	         bIndexFit,
	         bVertexFit = IMG_TRUE,
			 bDeindexCase = IMG_FALSE;

	/* Setup bIndexBO & bSourceBO */
	bIndexBO   = (psIndexBO) ? IMG_TRUE : IMG_FALSE;
	bSourceBO  = (psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ) ? IMG_TRUE : IMG_FALSE;

	/* We cannot use an index buffer object in these cases */
	if (eMode==GL_LINE_STRIP ||
		eMode==GL_LINE_LOOP  ||
		((eMode==GL_TRIANGLE_FAN) && (ui32NumIndices > MAX_INDICES_PER_PRIMBLOCK(ui32SizePerIndex))) ||	/* PRQA S 3356 */ /* Use this expression to keep code flexible as MAX_INDEX_ADDRESS may have different values. */
		eType==GL_UNSIGNED_BYTE)
	{
		bIndexBO = IMG_FALSE;
	}

	if(!bIndexBO && !bSourceBO)
	{
		if(ui32VertexCount > 2*ui32Count)
		{
			bDeindexCase = IMG_TRUE;
		}
	}

	if(ui32VertexCount * gc->ui32VertexSize + gc->ui32VertexRCSize + gc->ui32VertexAlignSize > MAX_VBUFFER)
	{
		if(gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32BufferLimitInBytes < gc->sAppHints.ui32MaxVertexBufferSize)
		{
			CircularBuffer *psBuffer;
			
			if(ScheduleTA(gc, gc->psRenderSurface, GLES2_SCHEDULE_HW_WAIT_FOR_TA) != IMG_EGL_NO_ERROR)
			{
				PVR_DPF((PVR_DBG_ERROR,"PickDrawElementsProc: ScheduleTA did not work properly"));
			}
			
			psBuffer = CBUF_CreateBuffer(gc->ps3DDevData, 
										CBUF_TYPE_VERTEX_DATA_BUFFER, 
										gc->psSysContext->hGeneralHeap, 
										gc->psSysContext->hSyncInfoHeap, 
										gc->psSysContext->sHWInfo.sMiscInfo.hOSGlobalEvent, 
										MIN(gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32BufferLimitInBytes * 2, gc->sAppHints.ui32MaxVertexBufferSize),
										gc->psSysContext->hPerProcRef);

			if(psBuffer)
			{
				PVR_DPF((PVR_DBG_WARNING,"PickDrawElementsProc: Resized dynamic vertex buffer. Was %u bytes, now %u bytes", 
					gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32BufferLimitInBytes, psBuffer->ui32BufferLimitInBytes));
				
				CBUF_DestroyBuffer(gc->ps3DDevData, gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]);

				gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER] = psBuffer;	
	
				/* Point the TA Kick status update to the device memory reserved for status updates in the buffer */
				gc->sKickTA.asTAStatusUpdate[CBUF_TYPE_VERTEX_DATA_BUFFER].hKernelMemInfo = gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->psStatusUpdateMemInfo->hKernelMemInfo;
				gc->sKickTA.asTAStatusUpdate[CBUF_TYPE_VERTEX_DATA_BUFFER].sCtlStatus.sStatusDevAddr.uiAddr = gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->psStatusUpdateMemInfo->sDevVAddr.uiAddr;

				/* set the read offset pointer in the buffer */
				gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->pui32ReadOffset = (IMG_UINT32*)gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->psStatusUpdateMemInfo->pvLinAddr;
			}
			else
			{
				PVR_DPF((PVR_DBG_ERROR,"PickDrawElementsProc: Failed to create larger dynamic vertex buffer"));
			}
		}

		if(ui32VertexCount * gc->ui32VertexSize + gc->ui32VertexRCSize  + gc->ui32VertexAlignSize> MAX_VBUFFER)
		{
			bVertexFit = IMG_FALSE;
		}
	}

	if(bIndexBO)
	{
		if(bVertexFit) 
		{
			drawVArray = DrawElementsIndexBO;	
			
			return drawVArray;
		}
	}
#if 0
	/* DrawVertexArray does the same as DrawElementsMemcpyIndices making the later redundant */
	else
	{
		bIndexFit = IMG_TRUE;
		if (ui32NumIndices * ui32SizePerIndex > MAX_IBUFFER)
		{
			bIndexFit = IMG_FALSE;
		}

		if(bVertexFit && 
			bIndexFit && 
			primDirectIndex[eMode] &&
			!bDeindexCase)
		{
			drawVArray = DrawElementsMemcpyIndices;

			return drawVArray;
		}
	}
#endif

	/* Recompute the number of indices needed, recheck the index buffer */
	ui32NumIndices = GetNumIndices(eMode, ui32Count);

	bIndexFit = IMG_TRUE; 

	if((ui32NumIndices * ui32SizePerIndex > MAX_IBUFFER) || (ui32MaxIndex > 64 * 1024)) 
	{
		/* Either some indices require more than 16 bits or the index buffer is too small */
		bIndexFit = IMG_FALSE;
	}

	if(bVertexFit && bIndexFit)
	{
		if(bDeindexCase)
		{
			drawVArray = DrawElementsDeindex;
		}
		else
		{
			drawVArray = DrawVertexArray;
		}
	}
#if 0
	else if(bVertexFit)
	{
		drawVArray = DrawBatchOnIBuffer;
	}
#endif
	else
	{
		drawVArray = DrawBatchOnVBuffer;
	}

	return drawVArray;
}


/***********************************************************************************
 Function Name      : DetermineMinAndMaxIndices
 Inputs             : gc
                      ui32Count - The number of indices in pvIndices
                      eType - One of GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT or GL_UNSIGNED_INT
                      pvIndices - Array of ui32Count indices of type eType.
 Outputs            : pui32MinIndex, pui32MaxIndex
 Returns            : 
 Description        : Determines the minimum and maximum indices that appear in the array pvIndices.
************************************************************************************/
static IMG_VOID DetermineMinAndMaxIndices(GLES2Context *gc, IMG_UINT32 ui32Count, GLenum eType,
										 const IMG_VOID *pvIndices, IMG_UINT32 *pui32MinIndex, IMG_UINT32 *pui32MaxIndex)
{
	IMG_UINT32 i, ui32MinIndex, ui32MaxIndex;
	const IMG_VOID *pvTmpIndices;
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES2BufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;

	/* Setup pvTmpIndices using the current VAO's bound element buffer object */
	if (psIndexBO) 
	{
	    GLES_ASSERT(psIndexBO->psMemInfo);

		/* If we are using an index buffer object, then pvIndices is only an offset to the beginning of the buffer */
		pvTmpIndices = psIndexBO->psMemInfo->pvLinAddr;
		pvTmpIndices = (const IMG_VOID *)((const IMG_UINT8 *)pvTmpIndices + GLES2_BUFFER_OFFSET(pvIndices));
	}
	else
	{
		pvTmpIndices = pvIndices;
	}	

	ui32MinIndex = 0xFFFFFFFF;
	ui32MaxIndex = 0;

	/* We can probably improve the performance of these loops doing branch removal, loop unrolling or both. */
	switch(eType) 
	{
		case GL_UNSIGNED_BYTE:
		{
			const IMG_UINT8 *pui8Elements = (const IMG_UINT8 *)pvTmpIndices;

			for (i=0; i < ui32Count; ++i)
			{
				if(pui8Elements[i] < ui32MinIndex)
				{
					ui32MinIndex = pui8Elements[i];
				}

				if(pui8Elements[i] > ui32MaxIndex)
				{
					ui32MaxIndex = pui8Elements[i];
				}
			}

			break;
		}
		case GL_UNSIGNED_SHORT:
		{
			const IMG_UINT16 *pui16Elements = (const IMG_UINT16 *)pvTmpIndices;

			for (i=0; i < ui32Count; ++i)
			{
				if(pui16Elements[i] < ui32MinIndex)
				{
					ui32MinIndex = pui16Elements[i];
				}

				if(pui16Elements[i] > ui32MaxIndex)
				{
					ui32MaxIndex = pui16Elements[i];
				}
			}

			break;
		}
		case GL_UNSIGNED_INT:
		{
			const IMG_UINT32 *pui32Elements = (const IMG_UINT32 *)pvTmpIndices;

			for (i=0; i < ui32Count; ++i)
			{
				if(pui32Elements[i] < ui32MinIndex)
				{
					ui32MinIndex = pui32Elements[i];
				}

				if(pui32Elements[i] > ui32MaxIndex)
				{
					ui32MaxIndex = pui32Elements[i];
				}
			}

			break;
		}
		default:
		{
			PVR_DPF((PVR_DBG_ERROR,"DetermineMinAndMaxIndices: Bad type (0x%X) - shouldn't be here", eType));
			return;
		}
	}

	*pui32MinIndex = ui32MinIndex;
	*pui32MaxIndex = ui32MaxIndex;
}


/***********************************************************************************
 Function Name      : TransformIndicesTo16Bits
 Inputs             : gc
                      ui32Count - Number of indices to transform
                      eType - Must be GL_UNSIGNED_BYTE
                      pvIndices - Pointer to the array of indices (of type eType)
 Outputs            : -
 Returns            : An array of ui32Count of IMG_UINT16 indices transformed from pvIndices.
 Description        : 
************************************************************************************/
static const IMG_UINT16* TransformIndicesTo16Bits(GLES2Context *gc, IMG_UINT32 ui32Count, GLenum eType, const IMG_VOID *pvIndices)
{
	IMG_UINT32 i;
	IMG_UINT16 *pui16OutIndices = GLES2Malloc(gc, sizeof(IMG_UINT16)*ui32Count);
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES2BufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;

	if(!pui16OutIndices)
	{
		PVR_DPF((PVR_DBG_MESSAGE,"TransformIndicesTo16Bits: Out of memory"));
		SetError(gc, GL_OUT_OF_MEMORY);
		return IMG_NULL;
	}

	/* Setup pvIndices using the current VAO's bound element buffer object */
	if (psIndexBO)
	{
		PVRSRV_CLIENT_MEM_INFO *psMemInfo = psIndexBO->psMemInfo;

		if(GLES2_BUFFER_OFFSET(pvIndices) > (GLintptr)psMemInfo->uAllocSize)
		{
			PVR_DPF((PVR_DBG_ERROR,"Index offset %ld is larger than index buffer size %u",GLES2_BUFFER_OFFSET(pvIndices), psMemInfo->uAllocSize));
		}
		/* The given 'indices' pointer is actually an offset when an index buffer object is bound */
		pvIndices = (const IMG_UINT8*)psMemInfo->pvLinAddr + GLES2_BUFFER_OFFSET(pvIndices);
	}

	/* Currently the only transformation we support is from 8-bit to 16-bit. If any other is needed, add it here */
	if(eType == GL_UNSIGNED_BYTE)
	{
		const IMG_UINT8 *pu8Indices = (const IMG_UINT8*)pvIndices;

		/* Promote from 8 bits to 16 bits */
		for(i = 0; i < ui32Count; ++i)
		{
			pui16OutIndices[i] = pu8Indices[i];
		}
	}
	else
	{
		PVR_DPF((PVR_DBG_MESSAGE,"TransformIndicesTo16Bits: Unsupported index type 0x%X", eType));
		GLES2Free(IMG_NULL, pui16OutIndices);
		pui16OutIndices = IMG_NULL;
	}

	return (const IMG_UINT16 *)pui16OutIndices;
}

/***********************************************************************************
 Function Name      : AttachTextureDependency
 Inputs             : gc, psTex
 Outputs            : -
 Returns            : -
 Description        : Attaches source sync dependency on the given texture to kick.
************************************************************************************/
static void AttachTextureDependency(GLES2Context *gc, GLES2Texture *psTex)
{
	PVRSRV_CLIENT_MEM_INFO *psMemInfo;
	IMG_UINT32 i;

#if defined(GLES2_EXTENSION_EGL_IMAGE)
	if(psTex->psEGLImageTarget && psTex->psEGLImageTarget->psMemInfo
			&& ((psTex->psEGLImageTarget->ui32Flags & EGLIMAGE_FLAGS_COMPOSITION_SYNC)
					|| (gc->sAppHints.bEnableAppTextureDependency)))
	{
		psMemInfo = psTex->psEGLImageTarget->psMemInfo;
	}
	else
#endif
#if defined(GLES2_EXTENSION_TEXTURE_STREAM)
	if(psTex->psBufferDevice)
	{
		psMemInfo = psTex->psBufferDevice->psBuffer[psTex->ui32BufferOffset].psBufferSurface;
	}
	else
#endif /* defined(GLES2_EXTENSION_TEXTURE_STREAM) */
	{
		return;
	}

	/* Don't add the dependency if we've already seen this sync object */
	for(i = 0; i < gc->psRenderSurface->ui32NumSrcSyncs; i++)
	{
		if(gc->psRenderSurface->apsSrcSurfSyncInfo[i] == psMemInfo->psClientSyncInfo)
		{
			return;
		}
	}

	/* FIXME: Do not attach the texture if we have run out of slots */
	if(gc->psRenderSurface->ui32NumSrcSyncs == SGX_MAX_SRC_SYNCS)
	{
		PVR_DPF((PVR_DBG_WARNING, "Ran out of sync objects on frame %u", gc->ui32FrameNum));
		return;
	}

	gc->psRenderSurface->apsSrcSurfSyncInfo[gc->psRenderSurface->ui32NumSrcSyncs] = psMemInfo->psClientSyncInfo;
	gc->psRenderSurface->ui32NumSrcSyncs++;
}

/***********************************************************************************
 Function Name      : AttachUsedTexturesToCurrentSurface
 Inputs             : gc, psShader
 Outputs            : -
 Returns            : -
 Description        : Attaches all textures used by the given shader to the current surface.
************************************************************************************/
static IMG_VOID AttachUsedTexturesToCurrentSurface(GLES2Context *gc, const GLES2ProgramShader *psShader)
{
	IMG_UINT32                i;
	const GLES2TextureSampler *psTextureSampler;
	IMG_UINT8                 ui8ImageUnit, ui8SamplerType;
	GLES2Texture              *psTex;

	/* FIXME: Code sadly copied from SetupTextureState. Any chance to reuse it? 
	          The problem is that SetupTextureState is only set if the state is dirty
	          whereas we want to attach textures to surfaces in every single usage.
	*/
	if(!psShader->ui32SamplersActive)
	{
		return;
	}

	for(i=0; i < GLES2_MAX_TEXTURE_UNITS; i++)
	{
		if(psShader->ui32SamplersActive & (1U << i))
		{
			psTextureSampler = &psShader->asTextureSamplers[i];
			ui8ImageUnit     = psTextureSampler->ui8ImageUnit;
			ui8SamplerType   = psTextureSampler->ui8SamplerTypeIndex;

			if(GLES2_IS_PERM_TEXTURE_UNIT(ui8ImageUnit))
			{
				/* FIXME - do something */
				continue;
			}
			else if(GLES2_IS_GRAD_TEXTURE_UNIT(ui8ImageUnit))
			{
				/* FIXME - do something */
				continue;
			}
			else
			{

				GLES_ASSERT(ui8ImageUnit < GLES2_MAX_TEXTURE_UNITS);
				GLES_ASSERT(ui8SamplerType < GLES2_TEXTURE_TARGET_MAX);

				/* PRQA S ??? 1 */ /* ui8ImageUnit has been prevalidated in glUniform1i */
				/* PRQA S ??? 1 */ /* ui8SamplerType has been prevalidated in glLinkProgram */
				psTex = gc->sTexture.apsBoundTexture[ui8ImageUnit][ui8SamplerType];
				GLES_ASSERT(psTex);

				if(psTex->bResidence)
				{
					AttachTextureDependency(gc, psTex);

					/* Attach this texture to the current frame of the current render surface */
					KRM_Attach(&gc->psSharedState->psTextureManager->sKRM, gc->psRenderSurface, 
							   &gc->psRenderSurface->sRenderStatusUpdate, &psTex->sResource);
				}
			}
		}
	}
}


/***********************************************************************************
 Function Name      : AttachAllUsedResourcesToCurrentSurface
 Inputs             : gc
 Outputs            : -
 Returns            : -
 Description        : Attaches all resources currently bound to the current surface using the
                      frame resource managers.
************************************************************************************/
static IMG_VOID AttachAllUsedResourcesToCurrentSurface(GLES2Context *gc)
{
	/* Attach the textures used in the vertex and in the fragment shader */
	AttachUsedTexturesToCurrentSurface(gc, &gc->sProgram.psCurrentProgram->sVertex);
	AttachUsedTexturesToCurrentSurface(gc, &gc->sProgram.psCurrentProgram->sFragment);

	/* Attach USSE fragment variants (set up by SetupUSEFragmentShader) */
	KRM_Attach(&gc->psSharedState->sUSEShaderVariantKRM, 
					  gc->psRenderSurface, &gc->psRenderSurface->sRenderStatusUpdate,
					  &gc->sProgram.psCurrentFragmentVariant->sResource);
}


/***********************************************************************************
 Function Name      : AttachAllUsedBOsAndVAOToCurrentKick
 Inputs             : gc
 Outputs            : -
 Returns            : -
 Description        : Sets up status writebacks for all Buffer Objects 
                      and the VAO used in current kick.
************************************************************************************/
IMG_INTERNAL IMG_VOID AttachAllUsedBOsAndVAOToCurrentKick(GLES2Context *gc)
{
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES2VertexArrayObject *psVAO = gc->sVAOMachine.psActiveVAO;

	GLES_ASSERT(VAO(gc));


	/* Add in the VAO if it possesses MemInfo for PDS program */
	if (psVAO->psMemInfo)
	{
		KRM_Attach(&gc->sVAOKRM, gc, &gc->sKRMTAStatusUpdate, &psVAO->sResource);
	}

	/* Add in any vertex buffer objects */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ)
	{
		IMG_UINT32 i;

		for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			GLES2AttribArrayPointerMachine *psAPMachine;
			GLES2BufferObject *psBufObj;
			
			psAPMachine = psVAOMachine->apsPackedAttrib[i];

			/* Assert psAPMachine exists */
			GLES_ASSERT(psAPMachine);
				
			psBufObj = psAPMachine->psState->psBufObj;

			if(psBufObj && !psAPMachine->bIsCurrentState)
			{
				KRM_Attach(&gc->psSharedState->sBufferObjectKRM, gc, &gc->sKRMTAStatusUpdate, &psBufObj->sResource);
			}
		}
	}

	/* Add in any index buffer objects */
	if (psVAOMachine->psBoundElementBuffer)
	{
		GLES2BufferObject *psBufObj;

		psBufObj = psVAOMachine->psBoundElementBuffer;

		KRM_Attach(&gc->psSharedState->sBufferObjectKRM, gc, &gc->sKRMTAStatusUpdate, &psBufObj->sResource);
	}
}


#if defined(FIX_HW_BRN_29546) || defined(FIX_HW_BRN_31728)
static IMG_VOID HandlePrimitiveTypeChange(GLES2Context *gc, GLenum mode)
{
#if defined(FIX_HW_BRN_29546)
	if((gc->sPrim.ePreviousPrimitiveType==GLES2_PRIMTYPE_SPRITE) || ((gc->sPrim.ePreviousPrimitiveType==GLES2_PRIMTYPE_POINT)))
	{
		/* Switch from points to non-points */
		if(mode!=GL_POINTS)
		{
			gc->ui32DirtyState |= (GLES2_DIRTYFLAG_RENDERSTATE | GLES2_DIRTYFLAG_FRAGMENT_PROGRAM);
		}
	}
	else
	{
		/* Switch from non-points to pointe */
		if(mode==GL_POINTS)
		{
			gc->ui32DirtyState |= (GLES2_DIRTYFLAG_RENDERSTATE | GLES2_DIRTYFLAG_FRAGMENT_PROGRAM);
		}
	}
#endif /* defined(FIX_HW_BRN_29546) */

#if defined(FIX_HW_BRN_31728)
	if((mode==GL_LINES) || (mode==GL_LINE_STRIP) || (mode==GL_LINE_LOOP))
	{
		if(!((gc->sPrim.eCurrentPrimitiveType==GLES2_PRIMTYPE_LINE)		 ||
		     (gc->sPrim.eCurrentPrimitiveType==GLES2_PRIMTYPE_LINE_LOOP) ||
		     (gc->sPrim.eCurrentPrimitiveType==GLES2_PRIMTYPE_LINE_STRIP)))
		{
            gc->ui32DirtyState |= (GLES2_DIRTYFLAG_RENDERSTATE | GLES2_DIRTYFLAG_FRAGMENT_PROGRAM);
		}
	}
	else
	{
		if(((gc->sPrim.eCurrentPrimitiveType==GLES2_PRIMTYPE_LINE)		||
		    (gc->sPrim.eCurrentPrimitiveType==GLES2_PRIMTYPE_LINE_LOOP) ||
		    (gc->sPrim.eCurrentPrimitiveType==GLES2_PRIMTYPE_LINE_STRIP)))
		{
            gc->ui32DirtyState |= (GLES2_DIRTYFLAG_RENDERSTATE | GLES2_DIRTYFLAG_FRAGMENT_PROGRAM);
		}
	}
#endif /* defined(FIX_HW_BRN_31728) */

	gc->sPrim.eCurrentPrimitiveType = mode;
}	
#endif /* defined(FIX_HW_BRN_29546) || defined(FIX_HW_BRN_31728) */

/***********************************************************************************
 Function Name      : glDrawArrays
 Inputs             : eMode, first, count
 Outputs            : -
 Returns            : -
 Description        : ENTRYPOINT: Draws primitives of type eMode using attrib arrays. 
					  Will validate as necessary, then send control, state and attrib 
					  data to HW.
************************************************************************************/
GL_APICALL void GL_APIENTRY glDrawArrays(GLenum mode, GLint first, GLsizei count)
{
	PFNDrawVArray pfnDrawArrays;
	IMG_UINT32 ui32NumIndices;
	IMG_UINT32 ui32NoClears = 0;
	GLES2VertexArrayObjectMachine *psVAOMachine;
	
	__GLES2_GET_CONTEXT();

	PVR_DPF((PVR_DBG_CALLTRACE,"glDrawArrays"));

	GLES2_TIME_START(GLES2_TIMES_glDrawArrays);
	GLES2_TIME_START(GLES2_TIMER_ARRAY_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));

	/* check that first and count are positive */
	if(first < 0 || count < 0) 
	{
		SetError(gc, GL_INVALID_VALUE);
	
		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));
		GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

		return;
	}

	if(mode > GL_TRIANGLE_FAN)
	{
		SetError(gc, GL_INVALID_ENUM);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));
		GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

		return;
	}
	
    ui32NumIndices = GetNumIndices(mode, (IMG_UINT32)count);

	if(count == 0 || ui32NumIndices == 0)
	{
		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

		return;
	}

	if(!gc->sProgram.psCurrentProgram || !gc->sProgram.psCurrentProgram->bSuccessfulLink)
	{
		SetError(gc, GL_INVALID_OPERATION);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

		return;
	}

	if(GetFrameBufferCompleteness(gc) != GL_FRAMEBUFFER_COMPLETE)
	{
		SetError(gc, GL_INVALID_FRAMEBUFFER_OPERATION);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

		return;
	}
	
	if ((gc->sState.sPolygon.eCullMode==GL_FRONT_AND_BACK) && ((gc->ui32Enables & GLES2_CULLFACE_ENABLE) != 0) &&
	   (mode!=GL_LINES) && (mode!=GL_LINE_LOOP) && (mode!=GL_LINE_STRIP) && (mode!=GL_POINTS))
	{
		/* Return if there's nothing to draw due to front_and_back culling */
		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

		return;
	}

	if ((mode==GL_POINTS) && ((gc->sProgram.psCurrentProgram->ui32OutputSelects & EURASIA_MTE_SIZE) == 0))
	{
		/* Return if there's no point size written by the shader and we're drawing points */
		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

		return;
	}

	if(!PrepareToDraw(gc, &ui32NoClears, IMG_TRUE))
	{
		PVR_DPF((PVR_DBG_ERROR,"glDrawArrays: Can't prepare to draw"));

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

		return;
	}

#if defined(FIX_HW_BRN_29546) || defined(FIX_HW_BRN_31728)
	HandlePrimitiveTypeChange(gc, mode);
#endif /* defined(FIX_HW_BRN_29546) || defined(FIX_HW_BRN_31728) */

	if(gc->ui32DirtyState || gc->sVAOMachine.psActiveVAO->ui32DirtyState)
	{
		if(ValidateState(gc)!=GLES2_NO_ERROR)
		{
			PVR_DPF((PVR_DBG_ERROR,"glDrawArrays: ValidateState() failed"));
			
			PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);
			
			GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
			GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

			return;
		}
	}

	/* Setup VAOMachine */
	psVAOMachine = &(gc->sVAOMachine);

	
	/* Check whether any buffer object has undefined memory. 
	   This could cause visual corruption or lockup, so return without drawing. 
	   An OUT_OF_MEMORY error will have been raised at the point of allocation failure.
	 */
	if (psVAOMachine->ui32ControlWord & ATTRIBARRAY_BAD_BUFOBJ)
	{
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

		return;
	}

	/* Check whether any buffer object is mapped */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_MAP_BUFOBJ)
	{
		SetError(gc, GL_INVALID_OPERATION);
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);
		
		return;
	}

	/* Attach all used resources to the current surface */
	AttachAllUsedResourcesToCurrentSurface(gc);

	pfnDrawArrays = PickDrawArraysProc(gc, mode, (IMG_UINT32)count);

	GLES_ASSERT(pfnDrawArrays != IMG_NULL);

	(*pfnDrawArrays)(gc, mode, (IMG_UINT32)first, (IMG_UINT32)count, ui32NumIndices, 0, IMG_NULL, (IMG_UINT32)first, (IMG_UINT32)count);

	/*
		Update vertex and index buffers committed primitive offset
	*/
	CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

	PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

	GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
	GLES2_TIME_STOP(GLES2_TIMES_glDrawArrays);

	GLES2_PROFILE_INCREMENT_DRAWARRAYS_CALLCOUNT(mode);
	GLES2_PROFILE_INCREMENT_DRAWARRAYS_VERTEXCOUNT(mode, count);

	GLES2_PROFILE_ADD_STATE_METRIC;
}


/***********************************************************************************
 Function Name      : glDrawElements
 Inputs             : eMode, count, eType indices
 Outputs            : -
 Returns            : -
 Description        : ENTRYPOINT: Draws indexed primitives of type eMode using attrib 
					  arrays and an index list. Will validate as necessary, then send 
					  control, state and attrib data to HW.
************************************************************************************/
GL_APICALL void GL_APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices)
{
	IMG_UINT32 ui32MinIndex=0xFFFFFFFF, ui32MaxIndex=0,ui32NumIndices;
	IMG_UINT32 ui32VertexStart, ui32VertexCount;
	PFNDrawVArray pfnDrawElements;
	const IMG_UINT16 *pui16Elements = (const IMG_UINT16 *)indices;
	IMG_BOOL bIndicesWerePromoted = IMG_FALSE;
	IMG_UINT32 ui32NoClears = 0;
	GLES2VertexArrayObjectMachine *psVAOMachine;

	__GLES2_GET_CONTEXT();

	PVR_DPF((PVR_DBG_CALLTRACE,"glDrawElements"));

	GLES2_TIME_START(GLES2_TIMES_glDrawElements);
	GLES2_TIME_START(GLES2_TIMER_ELEMENT_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));

	/* check that count is positive */
	if(count < 0) 
	{
		SetError(gc, GL_INVALID_VALUE);

		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));
		GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

		return;
	}

	if(mode > GL_TRIANGLE_FAN) 
	{
bad_enum:
		SetError(gc, GL_INVALID_ENUM);

		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));
		GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

		return;
	}

	switch(type) 
	{
		case GL_UNSIGNED_BYTE:
		case GL_UNSIGNED_SHORT:
		case GL_UNSIGNED_INT:
		{
			break;
		}
		default:
		{
			goto bad_enum;
		}
	}

	if(!gc->sProgram.psCurrentProgram || !gc->sProgram.psCurrentProgram->bSuccessfulLink)
	{
		SetError(gc, GL_INVALID_OPERATION);

		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

		return;
	}

	ui32NumIndices = GetNumIndices(mode, (IMG_UINT32)count);

	if(count == 0 || ui32NumIndices == 0)
	{
		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

		return;
	}

	if(GetFrameBufferCompleteness(gc) != GL_FRAMEBUFFER_COMPLETE)
	{
		SetError(gc, GL_INVALID_FRAMEBUFFER_OPERATION);

		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

		return;
	}

	if ((gc->sState.sPolygon.eCullMode==GL_FRONT_AND_BACK) && ((gc->ui32Enables & GLES2_CULLFACE_ENABLE) != 0) && 
	   (mode!=GL_LINES) && (mode!=GL_LINE_LOOP) && (mode!=GL_LINE_STRIP) && (mode!=GL_POINTS))
	{
		/* Return if there's nothing to draw due to front_and_back culling */
		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

		return;
	}

	if ((mode==GL_POINTS) && ((gc->sProgram.psCurrentProgram->ui32OutputSelects & EURASIA_MTE_SIZE) == 0))
	{
		/* Return if there's no point size written by the shader and we're drawing points */
		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

		return;
	}

	if(!PrepareToDraw(gc, &ui32NoClears, IMG_TRUE))
	{
		PVR_DPF((PVR_DBG_ERROR,"glDrawElements: Can't prepare to draw"));

		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

		return;
	}

#if defined(FIX_HW_BRN_29546) || defined(FIX_HW_BRN_31728)
	HandlePrimitiveTypeChange(gc, mode);
#endif /* defined(FIX_HW_BRN_29546) || defined(FIX_HW_BRN_31728) */

	if(gc->ui32DirtyState || gc->sVAOMachine.psActiveVAO->ui32DirtyState)
	{
		if(ValidateState(gc)!=GLES2_NO_ERROR)
		{
			PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);
			PVR_DPF((PVR_DBG_ERROR,"glDrawElements: ValidateState() failed"));
			
			GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
			GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

			return;
		}
	}

	/* Attach all used resources to the current surface */
	AttachAllUsedResourcesToCurrentSurface(gc);


	/* Setup VAOMachine */
	psVAOMachine = &(gc->sVAOMachine);

	/*  Check whether any buffer object has undefined memory. 
		This could cause visual corruption or lockup, so return without drawing. 
		An OUT_OF_MEMORY error will have been raised at the point of allocation failure.
	*/
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_BAD_BUFOBJ)
	{
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);
		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

		return;
	}

	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_MAP_BUFOBJ)
	{
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);
		SetError(gc, GL_INVALID_OPERATION);

		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);
		
		return;
	}

	if (VAO_IS_ZERO(gc)) /* This setup is only for the DEFAULT VAO */
	{
		if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_VARRAY)
		{
			DetermineMinAndMaxIndices(gc, (IMG_UINT32)count, type, indices, &ui32MinIndex, &ui32MaxIndex);

			ui32VertexStart = ui32MinIndex;
			ui32VertexCount = ui32MaxIndex - ui32MinIndex + 1;

			if(INDEX_BUFFER_OBJECT(gc) && (GL_UNSIGNED_BYTE != type)) 
			{
				ui32VertexCount += ui32VertexStart;
				ui32VertexStart = 0;
			}
		}
		else
		{
			/* Because all the vertex data comes from buffer objects (or is copied from current state), there is 
			 * no need to know the vertex range. */
			ui32VertexStart = 0;
			ui32VertexCount = 0;
		}
	}
	else /* The following setup is for the NON-ZERO VAO, which does not contain client arrays */
	{
		/* Because all the vertex data comes from buffer objects (or is copied from current state), there is 
		 * no need to know the vertex range. */
		ui32VertexStart = 0;
		ui32VertexCount = 0;
	}

	/* Pick draw functions */
	pfnDrawElements = PickDrawElementsProc(gc, mode, type, (IMG_UINT32)count, ui32VertexCount, ui32MaxIndex);

	GLES_ASSERT(pfnDrawElements != IMG_NULL);

	/* All 8-bit indices must be promoted to 16-bit */
	if(GL_UNSIGNED_BYTE == type)
	{
		pui16Elements = TransformIndicesTo16Bits(gc, (IMG_UINT32)count, type, indices);

		if(!pui16Elements)
		{
			goto StopTimerAndReturn;
		}

		bIndicesWerePromoted = IMG_TRUE;
		type = GL_UNSIGNED_SHORT;
	}
	else if (pfnDrawElements != DrawElementsIndexBO) /* To see whether can use sVAOMachine or sBufObjMachine 's element bufobj */
	{
		PVRSRV_CLIENT_MEM_INFO *psMemInfo = IMG_NULL;
		GLES2BufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;

		if (psIndexBO)
		{
			psMemInfo = psIndexBO->psMemInfo;
		}
		
		if (psMemInfo)
		{
			if(GLES2_BUFFER_OFFSET(indices) > (GLintptr)psMemInfo->uAllocSize)
			{
				PVR_DPF((PVR_DBG_ERROR,"Index offset %ld is larger than index buffer size %u",GLES2_BUFFER_OFFSET(indices), psMemInfo->uAllocSize));
			}

			/* The given 'indices' pointer is actually an offset when an index buffer object is bound */
			pui16Elements = (const IMG_UINT16*)((IMG_UINTPTR_T)((const IMG_UINT8 *)psMemInfo->pvLinAddr + GLES2_BUFFER_OFFSET(indices)));
		}
	}

	/* Call the actual draw element function */
	(*pfnDrawElements)(gc, mode, 0, (IMG_UINT32)count, ui32NumIndices, type, pui16Elements, ui32VertexStart, ui32VertexCount); 

	if(bIndicesWerePromoted) 
	{
		GLES2Free(IMG_NULL, (IMG_VOID *)((IMG_UINTPTR_T)pui16Elements));
	}

	/*
		Update vertex and index buffers committed primitive offset
	*/
	CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

StopTimerAndReturn:
	PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

	GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
	GLES2_TIME_STOP(GLES2_TIMES_glDrawElements);

	GLES2_PROFILE_INCREMENT_DRAWELEMENTS_CALLCOUNT(mode);
	GLES2_PROFILE_INCREMENT_DRAWELEMENTS_VERTEXCOUNT(mode, count);

	GLES2_PROFILE_ADD_STATE_METRIC;
}

#if defined(GLES2_EXTENSION_MULTI_DRAW_ARRAYS)
/*
 * Implementation of GL_EXT_multi_draw_arrays
 *
 */

typedef IMG_VOID (*PFNMultiDrawVArray) (GLES2Context *gc, GLenum eMode, IMG_UINT32 * ui32First, IMG_UINT32 * pui32Count,
										IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID * *elements,
										IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
										IMG_UINT32 ui32PrimCount);


static IMG_VOID MultiDrawArraysAutoIndices(GLES2Context *gc, GLenum eMode, IMG_UINT32 * pui32First, IMG_UINT32 * pui32Count,
										   IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID * *elements,
										   IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
										   IMG_UINT32 ui32PrimCount)
{
	IMG_UINT32 i, j; 

	/* This function autogenerates the indices */
	GLES_ASSERT(!elements);
	PVR_UNREFERENCED_PARAMETER(elements);
	PVR_UNREFERENCED_PARAMETER(pui32First);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(eMode);

	/* The type must be natively supported by the hardware */
	GLES_ASSERT(primDirectIndex[eType]);

	if(ui32VertexCount<=GLES2_NUM_STATIC_INDICES && CreateSequentialStaticIndices(gc))
	{
		/* Check the vertex buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, sizeof(IMG_UINT16));
		
		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		for(i = 0; i < ui32PrimCount; i++)
		{
			IMG_DEV_VIRTADDR sIndicesVAddrBase;
			
			sIndicesVAddrBase.uiAddr = gc->psSharedState->psSequentialStaticIndicesMemInfo->sDevVAddr.uiAddr;

			sIndicesVAddrBase.uiAddr += (pui32First[i] - ui32VertexStart)*sizeof(IMG_UINT16);  
			
			/* Emit state */
			GLES2EmitState(gc, eMode, IMG_FALSE, pui32Count[i], sIndicesVAddrBase, 0);
		}
	}
	else
	{
		IMG_UINT16 *pui16Indices, *pui16IndexBase;
		IMG_UINT32 ui32IndexDWords;

		/* Check the vertex and index buffer space, flush when necessary */
		/* We are adding Primcount indices, so that if there is an odd number of indices in a primitive, we will definitely have
		 * enough space to waste 16 bits per primitive when we update the bufferpos in DWORDS increments.
		 */
		GetVertexIndexBufferSpace(gc, ui32NumIndices + ui32PrimCount, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		for(i = 0; i < ui32PrimCount; i++)
		{	
			GLES2_TIME_START(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);

			/* Round up to nearest 32 bits */
			ui32IndexDWords = (pui32Count[i] + 1) >> 1;

			pui16IndexBase = pui16Indices = (IMG_UINT16 *)CBUF_GetBufferSpace(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);
			if (pui16IndexBase == IMG_NULL)
			{
				PVR_DPF((PVR_DBG_ERROR,"MultiDrawArraysAutoIndices: Cannot get enough space for indices for primitive %d ", i));
				GLES2_TIME_STOP(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);
				break;
			}

			/* 
				write indices for current primitive; we can't do this for all primitives in one go
				because we have to take into account the case where there are gaps between sets of indices, e.g.:
					prim 1: 0, 1, 2
					prim 2: 5, 6, 7
				In this example there is a gap of two indices (3 and 4), but the index buffer has only six elements.
			*/
			for(j = 0; j < pui32Count[i]; j++)
			{
				*pui16Indices++ = (IMG_UINT16)((j + (pui32First[i] - ui32VertexStart))); 	
			}

			GLES2_TIME_STOP(GLES2_TIMER_INDEX_DATA_GENERATE_COPY);			
			
			CBUF_UpdateBufferPos(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER);
		
			/* Emit state */
			GLES2EmitState(gc, eMode, IMG_FALSE, pui32Count[i], CBUF_GetBufferDeviceAddress(gc->apsBuffers, (IMG_UINT32 *)((IMG_UINTPTR_T)pui16IndexBase), CBUF_TYPE_INDEX_DATA_BUFFER), 0);

			GLES2_INC_COUNT(GLES2_TIMER_INDEX_DATA_COUNT, ui32IndexDWords);
		}
	}
}

static IMG_VOID MultiDrawBatchOnVBuffer(GLES2Context *gc, GLenum eMode, IMG_UINT32 * ui32First, IMG_UINT32 * pui32CountIn,
										   IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID * *elements,
										   IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
										   IMG_UINT32 ui32PrimCount)
{
	IMG_UINT32 i;
	IMG_UINT32 ui32IndexStart;
	IMG_UINT32 ui32RewindCount;
	IMG_UINT32 ui32BatchSize; 
	const IMG_UINT16 *pui16Indices = (const IMG_UINT16 *)elements;
	const IMG_UINT32 *pui32Indices = (const IMG_UINT32 *)elements;
	IMG_BOOL bAreIndices32Bit = (eType == GL_UNSIGNED_INT)? IMG_TRUE:IMG_FALSE;

	GLES2AttribArrayPointerMachine *psAPMachine;
	GLES2BufferObject *apsBufObjs[GLES2_MAX_VERTEX_ATTRIBS];
	IMG_BOOL bDeIndex = (elements)?IMG_TRUE : IMG_FALSE;
	IMG_BOOL bAttributesWereMangled = IMG_FALSE;

	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);

	GLES_ASSERT(VAO(gc));

	PVR_UNREFERENCED_PARAMETER(ui32VertexStart);
	PVR_UNREFERENCED_PARAMETER(ui32VertexCount);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32NumIndices);

	/*
	 * If any of the attributes comes from a buffer object,
	 * we have to reset the stream pointers because we are going to de-index.
	 * This is our way to punish the bad bad app that forced us to go through this code path.
	 */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ)
	{
		bAttributesWereMangled = IMG_TRUE;
		/*
		 * Make a backup copy of the buffer objects pointers of the attributes.
		 * Set up the attrib pointers to force CopyArrayDataDeIndex to copy the vertex attributes from
		 * the buffer objects into the vertex buffer.
		 */
		for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			/* Get psAPMachine & Assert it exists */
			psAPMachine = psVAOMachine->apsPackedAttrib[i];
			GLES_ASSERT( psAPMachine );
				
			apsBufObjs[i] = psAPMachine->psState->psBufObj;
				
			if(apsBufObjs[i])
			{
				psAPMachine->psState->psBufObj = 0;
			}
		}
		
		/* Only set context's dirty state, no need for VAO */
		gc->ui32DirtyState |= GLES2_DIRTYFLAG_VAO_ATTRIB_STREAM;


		/* Validate all the states at this point */
		if(ValidateState(gc)!=GLES2_NO_ERROR)
		{
			PVR_DPF((PVR_DBG_ERROR,"MultiDrawBatchOnVBuffer: ValidateState() failed"));
				
			goto ResetAttribsAndReturn;
		}

		for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			/* Get psAPMachine & Assert it exists */
			psAPMachine = psVAOMachine->apsPackedAttrib[i];
			GLES_ASSERT( psAPMachine );

			/* The value of pui8CopyPointer is actually an offset based on the buffer object,
			   so we need to add it to get a real pointer. */
			if(apsBufObjs[i] && !psAPMachine->bIsCurrentState)
			{
				psAPMachine->pui8CopyPointer += (IMG_UINT32)apsBufObjs[i]->psMemInfo->pvLinAddr;
			}
		}
	}


	for(i = 0; i < ui32PrimCount; i++)
	{
		ui32BatchSize  = pui32CountIn[i];
		ui32IndexStart = 0;

		/* work out the batch size */
		if(gc->ui32VertexSize)
		{
			ui32BatchSize = MIN(ui32BatchSize, (MAX_VBUFFER - gc->ui32VertexRCSize - gc->ui32VertexAlignSize) / gc->ui32VertexSize);
		}

		ui32BatchSize = MIN(ui32BatchSize, MAX_IBUFFER / (sizeof(IMG_UINT16)*primIndexMult[eMode]));	/* PRQA S 3689 */ /* eMode is made sure to be a valid index before passing into this function. */
		ui32BatchSize = MIN(64*1024, ui32BatchSize); 

		/* 
		 * After the first batch has been drawn, it needs some vertices overlaped for the second batch, 
		 * the following function calculates this rewind count, at the same time, modifying uBatchSize
		 * to a proper figure.
		 */
		if(ui32BatchSize < pui32CountIn[i])
		{
			ui32RewindCount = GetVArrayBatchRewindCount(eMode, &ui32BatchSize);
		}
		else
		{
			ui32RewindCount = 0;
		}


		switch(eMode)
		{
			case GL_POINTS:          /* Intentional fall-through */
			case GL_TRIANGLES:
			case GL_LINES:
			case GL_TRIANGLE_STRIP:
			case GL_LINE_STRIP:
			case GL_LINE_LOOP:
			{
				IMG_UINT32 ui32Count, ui32BatchIndices;

				do
				{	
					ui32Count = MIN(ui32BatchSize, pui32CountIn[i] - ui32IndexStart);
					ui32BatchIndices = GetNumIndices(eMode, ui32Count);

					if(ui32BatchIndices)
					{
						/*check the vertex and index buffer space, flush when necessary */
						GetVertexIndexBufferSpace(gc, ui32BatchIndices, ui32Count, sizeof(IMG_UINT16));

						if(bDeIndex)
						{
							CopyVArrayDataDeindex(gc, ui32IndexStart, ui32Count, (const IMG_VOID *) elements[i], bAreIndices32Bit);	/* PRQA S 0505 */ /* elements cannot be NULL when bDeIndex is true. */
						}
						else
						{
							CopyVArrayData(gc, ui32IndexStart + ui32First[i], ui32Count, IMG_TRUE);
						}

						if(GL_LINE_LOOP == eMode)
						{
							/*
							* We override the line mode because we don't want to connect the last vertex of each
							* batch to the first vertex of each bach.
							*/
							(WriteIndices[0][GL_LINE_STRIP])(gc, GL_LINE_STRIP, 0, ui32Count, IMG_NULL);
						}
						else
						{
							(WriteIndices[0][eMode])(gc, eMode, 0, ui32Count, IMG_NULL);
						}
					}
				
					ui32IndexStart += ui32Count - ui32RewindCount;
			
					CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

				} while(ui32IndexStart + ui32RewindCount< pui32CountIn[i]);

				if(GL_LINE_LOOP == eMode)
				{
					/* Connect the last vertex with the first one */
					IMG_UINT32 pui32TmpIndex[2];
					IMG_UINT32 ui32PreviousIndex = (ui32RewindCount == 0) ? ui32IndexStart - 1 : ui32IndexStart;
				
					/* Check the vertex and index buffer space, flush when necessary */
					GetVertexIndexBufferSpace(gc, 2, 2, sizeof(IMG_UINT32));

					if(elements)
					{
						if(bAreIndices32Bit)
						{
							pui32TmpIndex[0] = pui32Indices[ui32PreviousIndex+ui32First[i]];
							pui32TmpIndex[1] = pui32Indices[ui32First[i]];
						}
						else
						{
							pui32TmpIndex[0] = pui16Indices[ui32PreviousIndex+ui32First[i]];
							pui32TmpIndex[1] = pui16Indices[ui32First[i]];
						}
					}
					else
					{
						pui32TmpIndex[0] = ui32PreviousIndex + ui32First[i];
						pui32TmpIndex[1] = ui32First[i];
					}

					CopyVArrayDataDeindex(gc, 0, 2, pui32TmpIndex, IMG_TRUE);

					(WriteIndices[1][GL_LINE_STRIP])(gc, GL_LINE_STRIP, 0, 2, IMG_NULL);

					CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);
				}

				break;
			}
			case GL_TRIANGLE_FAN:
			{
				IMG_UINT32 * IMG_RESTRICT pui32TmpIndex;
				IMG_UINT32 j, ui32Count, ui32BatchIndices;

				pui32TmpIndex = GLES2Malloc(gc, ui32BatchSize * sizeof(IMG_UINT32));

				if(!pui32TmpIndex)
				{
					PVR_DPF((PVR_DBG_WARNING,"MultiDrawBatchOnVBuffer: Out of memory for pui32TmpIndex"));

					SetError(gc, GL_OUT_OF_MEMORY);

					goto ResetAttribsAndReturn;
				}

				do
				{
					ui32Count = MIN(ui32BatchSize, pui32CountIn[i] - ui32IndexStart);
					ui32BatchIndices = GetNumIndices(eMode, ui32Count);

					if(ui32BatchIndices)
					{
						/* Check the vertex and index buffer space, flush when necessary */
						GetVertexIndexBufferSpace(gc, ui32Count, ui32Count, sizeof(IMG_UINT32));

						/* Check that we don't overflow pui32TmpIndex */
						GLES_ASSERT(ui32Count <= ui32BatchSize);
				
						if(elements)
						{
							/* for DrawElements */
							if(bAreIndices32Bit)
							{
								const IMG_UINT32 *pui32Src = &pui32Indices[ui32IndexStart + ui32First[i]];

								GLES2MemCopy(pui32TmpIndex, pui32Src, ui32Count*sizeof(IMG_UINT32) );

								pui32TmpIndex[0] = pui32Indices[ui32First[i]];
							}
							else
							{
								const IMG_UINT16 *pui16Src = &pui16Indices[ui32IndexStart + ui32First[i]];

								pui32TmpIndex[0] = pui16Indices[ui32First[i]];

								for(j = 1; j < ui32Count; ++j)
								{
									pui32TmpIndex[j] = pui16Src[j];
								}
							}
						}
						else
						{
							/* for DrawArrays */
							pui32TmpIndex[0] = (IMG_UINT16) ui32First[i];

							for(j = 1; j < ui32Count; ++j)
							{
								pui32TmpIndex[j] = (IMG_UINT16)(ui32IndexStart + ui32First[i] + j);
							}
						}

						CopyVArrayDataDeindex(gc, 0, ui32Count, pui32TmpIndex, IMG_TRUE);

						(WriteIndices[1][eMode])(gc, eMode, 0, ui32Count, IMG_NULL);
					}

					ui32IndexStart += (ui32Count - ui32RewindCount);
				
					CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

				} while(ui32IndexStart + ui32RewindCount < pui32CountIn[i]);

				GLES2Free(IMG_NULL, pui32TmpIndex);

				break;
			}
			default:
			{
				PVR_DPF((PVR_DBG_ERROR,"MultiDrawBatchOnVBuffer: Bad mode (0x%X) - shouldn't be here", eMode));
			}
		} /* switch */

	} /* for loop */

ResetAttribsAndReturn:

	if(bAttributesWereMangled)
	{
		/* Restore the pointers to buffer objects and 
		   set up the dirty flags to recompute the stream pointers. */
	    for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			psVAOMachine->apsPackedAttrib[i]->psState->psBufObj = apsBufObjs[i];
		}
	  
		/* Only set context's dirty state, no need for VAO's */
		gc->ui32DirtyState |= GLES2_DIRTYFLAG_VAO_ATTRIB_STREAM;
	}
}



static IMG_VOID MultiDrawVertexArray(GLES2Context *gc, GLenum eMode, IMG_UINT32 * ui32First, IMG_UINT32 * pui32Count,
									 IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID * *elements,
									 IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
									 IMG_UINT32 ui32PrimCount)
{
	IMG_UINT32 i, ui32IndexDWords;
	IMG_BOOL   bIndicesAreZeroBased = (elements==IMG_NULL)?IMG_TRUE:IMG_FALSE;

	GLES_ASSERT(!elements || eType != GL_UNSIGNED_BYTE);
	PVR_UNREFERENCED_PARAMETER(eType);

	if (bIndicesAreZeroBased && eMode==GL_LINE_STRIP &&
		((ui32VertexCount*2)<=GLES2_NUM_STATIC_INDICES) && CreateLineStripStaticIndices(gc))
	{
		/* Check the vertex buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		for(i = 0; i < ui32PrimCount; i++)
		{
			IMG_DEV_VIRTADDR sIndicesVAddrBase;
			IMG_UINT32 ui32AdjustedCount, ui32AdjustedStart;
			
			ui32AdjustedStart = (ui32First[i] - ui32VertexStart) * 2;
			ui32AdjustedCount = GetNumIndices(GL_LINE_STRIP, pui32Count[i]);

			sIndicesVAddrBase.uiAddr = gc->psSharedState->psLineStripStaticIndicesMemInfo->sDevVAddr.uiAddr;

			sIndicesVAddrBase.uiAddr += (ui32AdjustedStart * sizeof(IMG_UINT16));  

			/* Emit state */
			GLES2EmitState(gc, eMode, IMG_FALSE, ui32AdjustedCount, sIndicesVAddrBase, 0);
		}
	}
	else
	{
		IMG_UINT32 ui32Write32BitIndices = 0;

		if(elements && (eType == GL_UNSIGNED_INT))
		{
			ui32Write32BitIndices = 1;
		}
		
		/* Check the vertex and index buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, ui32NumIndices, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, bIndicesAreZeroBased);
		
		for(i = 0; i < ui32PrimCount; i++)
		{
			if(i > 0)
			{
				if(eMode == GL_LINE_LOOP || eMode == GL_LINE_STRIP)
				{
					ui32IndexDWords = (pui32Count[i]*2 + 2) >> 1;
				} 
				else
				{
					/* Round up mem usage to nearest 32 bits */
					ui32IndexDWords = (pui32Count[i] + 1) >> 1;
				}

				/* update position in the circular buffer */
				gc->pui32IndexData = CBUF_GetBufferSpace(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);
			}

			/* Write indices, may be 16 or 32-bit */
			/* PRQA S 0505 1 */ /* elements cannot be NULL when bIndicesAreZeroBased is false. */
			(WriteIndices[ui32Write32BitIndices][eMode])(gc, eMode, bIndicesAreZeroBased? (ui32First[i] - ui32VertexStart):0, pui32Count[i], bIndicesAreZeroBased?IMG_NULL:elements[i]);
	 
		}
	}
}

static IMG_VOID MultiDrawElementsDeindex(GLES2Context *gc, GLenum eMode, IMG_UINT32 * ui32First, IMG_UINT32 * pui32Count,
										 IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID * *elements,
										 IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
										 IMG_UINT32 ui32PrimCount)
{
	IMG_BOOL b32BitIndices = (eType==GL_UNSIGNED_INT) ? IMG_TRUE : IMG_FALSE;
	IMG_UINT32 i, ui32IndexDWords, j, ui32Offset = 0;
	IMG_UINT16 * pui16Tmpindex;
	IMG_UINT16 * pui16DeIndices;
	const IMG_UINT16 ** pui16Elements;

	GLES_ASSERT(VAO(gc));

	GLES_ASSERT(elements);

	PVR_UNREFERENCED_PARAMETER(ui32First);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32VertexStart);
	PVR_UNREFERENCED_PARAMETER(ui32VertexCount);


	/* We should not deindex BOs unless v and i data cannot fit in */
	GLES_ASSERT(!(gc->sVAOMachine.ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ));

	/* Check the vertex and index buffer space, flush when necessary */
	GetVertexIndexBufferSpace(gc, ui32NumIndices, ui32VertexCount, sizeof(IMG_UINT16));

	/*
	 * Copy all indices in pui16DeIndices, then use it to gather together all the vertices
	 * we need to draw 
	 */
	pui16Elements = (const IMG_UINT16 * *) elements;
	pui16DeIndices = GLES2Malloc(gc, ui32NumIndices * sizeof(IMG_UINT16));

	if(!pui16DeIndices)
	{
		PVR_DPF((PVR_DBG_WARNING,"MultiDrawElementsDeindex Out of memory for pui16DeIndices"));
		SetError(gc, GL_OUT_OF_MEMORY);
		return;
	}

	pui16Tmpindex = pui16DeIndices;

	for(i = 0; i < ui32PrimCount; i++)
	{
		const IMG_UINT16 * Temp = pui16Elements[i];

		for(j = 0; j < pui32Count[i]; j++)
		{
			*pui16Tmpindex = Temp[j];
			pui16Tmpindex++;
		}
	}
	
	CopyVArrayDataDeindex(gc, 0, ui32NumIndices, (IMG_VOID *) pui16DeIndices, b32BitIndices);


	for(i = 0; i < ui32PrimCount; i++)
	{
		if(i > 0)
		{
			if(eMode == GL_LINE_LOOP || eMode == GL_LINE_STRIP)
			{
				ui32IndexDWords = (pui32Count[i]*2 + 2) >> 1;
			} 
			else
			{
				/* Round up mem usage to nearest 32 bits */
				ui32IndexDWords = (pui32Count[i] + 1) >> 1;
			}

			/* update position in the circular buffer */
			gc->pui32IndexData = CBUF_GetBufferSpace(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);
		}

		/* Write 16-bit indices */
		(WriteIndices[0][eMode])(gc, eMode, ui32Offset, pui32Count[i], IMG_NULL);

		ui32Offset += pui32Count[i];
	}

	GLES2Free(IMG_NULL, pui16DeIndices);
}


static IMG_VOID MultiDrawElementsIndexBO(GLES2Context *gc, GLenum eMode, IMG_UINT32 * ui32First, IMG_UINT32 * pui32Count,
										 IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID * *elements,
										 IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
										 IMG_UINT32 ui32PrimCount)
{
    PVRSRV_CLIENT_MEM_INFO *psMemInfo;
	IMG_UINT32 ui32Offset; 
	IMG_DEV_VIRTADDR uIndexAddress;
	IMG_UINT32 ui32BatchSize, ui32RewindCount, ui32Count, ui32IndexStart, i;
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES2BufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;

	PVR_UNREFERENCED_PARAMETER(eMode);
	PVR_UNREFERENCED_PARAMETER(ui32First);
	PVR_UNREFERENCED_PARAMETER(ui32NumIndices);
	PVR_UNREFERENCED_PARAMETER(eType);

	GLES_ASSERT(psIndexBO->psMemInfo);

	psMemInfo = psIndexBO->psMemInfo;

	/* Check the vertex and index buffer space, flush when necessary */
	GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, 0);

	/* Setup stream addresses and copy the vertices attributes if required */
	CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_FALSE);

	
   	for(i = 0; i < ui32PrimCount; i++)
	{
		ui32Offset = (IMG_UINT32)GLES2_BUFFER_OFFSET(elements[i]);

		uIndexAddress.uiAddr = psMemInfo->sDevVAddr.uiAddr;
		uIndexAddress.uiAddr += (IMG_UINT32)ui32Offset;
		
		if(ui32Offset > psMemInfo->uAllocSize)
		{
			PVR_DPF((PVR_DBG_ERROR,"Index offset %u is larger than index buffer size %u", ui32Offset, psMemInfo->uAllocSize));
		}
		
		/* lock the circular buffer - for the first iteration it's done by GetVertexIndexBufferSpace */
		if(i > 0)
		{
			CBUF_GetBufferSpace(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);
		}
		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		/* 
		 * After the first batch has been drawn, it needs some vertices overlaped for the second batch, 
		 * the following function calculates this rewind count, at the same time, modifying uBatchSize
		 * to a proper figure.
		 */
		
		ui32BatchSize = MIN(pui32Count[i], MAX_INDICES_PER_PRIMBLOCK(sizeof(IMG_UINT16)));

		/* check if indices batching is needed */
		if(ui32BatchSize < pui32Count[i])
		{
			ui32RewindCount = GetVArrayBatchRewindCount(eMode, &ui32BatchSize);
		}
		else
		{
			ui32RewindCount = 0;
		}
		ui32IndexStart = 0;

		do
		{	
			ui32Count = MIN(ui32BatchSize, pui32Count[i] - ui32IndexStart);
			
			/* Emit state */
			GLES2EmitState(gc, eMode, (eType==GL_UNSIGNED_INT) ? IMG_TRUE : IMG_FALSE, ui32Count, uIndexAddress, 0);
			
			ui32IndexStart += ui32Count - ui32RewindCount;
			uIndexAddress.uiAddr += ((ui32Count - ui32RewindCount) * sizeof(IMG_UINT16));
			
		} while(ui32IndexStart + ui32RewindCount < pui32Count[i]);
	}
}

static PFNMultiDrawVArray PickMultiDrawArraysProc(GLES2Context *gc, GLenum eMode, IMG_UINT32 ui32Count)
{
	PFNMultiDrawVArray multiDrawVArray;

	IMG_BOOL bVertexFit = IMG_TRUE;
	IMG_BOOL bIndexFit = IMG_TRUE;

	if((ui32Count * sizeof(IMG_UINT16)) > MAX_IBUFFER || (ui32Count > 64*1024))
	{
		bIndexFit = IMG_FALSE;
	}

	if((ui32Count * gc->ui32VertexSize + gc->ui32VertexRCSize + gc->ui32VertexAlignSize) > MAX_VBUFFER)
	{
		bVertexFit = IMG_FALSE;
	}

	if(bVertexFit && 
	   bIndexFit && 
	   primDirectIndex[eMode])
	{
		multiDrawVArray = MultiDrawArraysAutoIndices;

		return multiDrawVArray;
	}

	if(bVertexFit && bIndexFit)
	{
		multiDrawVArray = MultiDrawVertexArray;
	}
	else
	{
		multiDrawVArray = MultiDrawBatchOnVBuffer;
	}

	return multiDrawVArray;
}



static PFNMultiDrawVArray PickMultiDrawElementsProc(GLES2Context *gc, GLenum eMode, GLenum eType,
													IMG_UINT32 ui32NumIndices, IMG_UINT32 ui32VertexCount, IMG_UINT32 ui32MaxIndex)
{
	IMG_UINT32 ui32SizePerIndex = sizeof(IMG_UINT16);
	PFNMultiDrawVArray multiDrawVArray;
	IMG_BOOL bIndexBO,
	         bSourceBO,
	         bIndexFit  = IMG_TRUE,
	         bVertexFit = IMG_TRUE,
			 bDeindexCase = IMG_FALSE;
	GLES2VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES2BufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;		

	/* Setup bIndexBO & bSourceBO */
	bIndexBO  = (psIndexBO) ? IMG_TRUE : IMG_FALSE;
	bSourceBO = (psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ) ? IMG_TRUE : IMG_FALSE;

	/* We cannot use an index buffer object in these cases */
	if (eMode==GL_LINE_STRIP ||
		eMode==GL_LINE_LOOP  ||
		((eMode==GL_TRIANGLE_FAN) && (ui32NumIndices > MAX_INDICES_PER_PRIMBLOCK(ui32SizePerIndex))) ||	/* PRQA S 3356 */ /* Use this expression to keep code flexible as MAX_INDEX_ADDRESS may have different values. */
		eType==GL_UNSIGNED_BYTE)
	{
		bIndexBO = IMG_FALSE;
	}

	if(!bIndexBO && !bSourceBO)
	{
		if(ui32VertexCount > 2*ui32NumIndices)
		{
			bDeindexCase = IMG_TRUE;
		}
	}


	if(!bIndexBO && ((ui32NumIndices * ui32SizePerIndex > MAX_IBUFFER) || (ui32MaxIndex > 64 * 1024)))
	{
		bIndexFit = IMG_FALSE;
	}

	if(ui32VertexCount * gc->ui32VertexSize + gc->ui32VertexRCSize + gc->ui32VertexAlignSize > MAX_VBUFFER)
	{
		bVertexFit = IMG_FALSE;
	}

	if(bIndexBO)
	{
		if(bVertexFit) 
		{
			multiDrawVArray = MultiDrawElementsIndexBO;	
			
			return multiDrawVArray;
		}
	}


	if(bVertexFit && bIndexFit)
	{
		if(bDeindexCase)
		{
			multiDrawVArray = MultiDrawElementsDeindex;
		}
		else
		{
			multiDrawVArray = MultiDrawVertexArray;
		}
	}
	else
	{
		multiDrawVArray = MultiDrawBatchOnVBuffer;
	}

	return multiDrawVArray;
}


/***********************************************************************************
 Function Name      : glMultiDrawArraysEXT
 Inputs             : mode, first, count, primcount
 Outputs            : -
 Returns            : -
 Description        : ENTRYPOINT: Draws primcount primitives of type mode using attrib arrays. 
					  Will validate as necessary, then send control, state and attrib 
					  data to HW.
************************************************************************************/
GL_API_EXT void GL_APIENTRY glMultiDrawArraysEXT(GLenum mode, GLint * first, GLsizei * count, GLsizei primcount)
{
	PFNMultiDrawVArray pfnMultiDrawArrays;
	IMG_UINT32 ui32NumIndices = 0;
	IMG_UINT32 ui32NoClears = 0;
	IMG_UINT32 ui32MinFirst = 0xFFFFFFFF, ui32MaxCount = 0;
	IMG_INT32 i;
	IMG_UINT32 * pui32ActualCount;
	IMG_BOOL bIsHWSupported = primDirectIndex[mode];
	GLES2VertexArrayObjectMachine *psVAOMachine;


	__GLES2_GET_CONTEXT();

	PVR_DPF((PVR_DBG_CALLTRACE,"glMultiDrawArrays"));

	GLES2_TIME_START(GLES2_TIMES_glMultiDrawArrays);
	GLES2_TIME_START(GLES2_TIMER_ARRAY_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));

	if(primcount < 0) 
	{
bad_value:
		SetError(gc, GL_INVALID_VALUE);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}

	if(primcount == 0)
	{
		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}
	
	for(i = 0; i < primcount; i++)
	{
		/* check that first and count are positive */
		if(first[i] < 0 || count[i] < 0) 
		{
			goto bad_value;
		}
	}

	if(mode > GL_TRIANGLE_FAN) 
	{
		SetError(gc, GL_INVALID_ENUM);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}

	pui32ActualCount = GLES2Malloc(gc, sizeof(IMG_UINT32)*(IMG_UINT32)primcount);

	if(!pui32ActualCount)
	{
		PVR_DPF((PVR_DBG_WARNING,"glMultiDrawArrays: Out of memory for ActualCount"));

		SetError(gc, GL_OUT_OF_MEMORY);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}

	for(i = 0; i < primcount; i++)
	{  
		/*  
			this if-else is to cope with an asimmetry:
			- sub-routines for HW supported primitives require adjusted count values,e.g.:
			 2 triangles, primcount = 2, count[0] = 4, count[1] = 5
			 In this case we want actualcount[0] = 3, actualcount[1] = 3, numindices = 9
			- sub-routines for non HW supported primitives (i.e., line strips and loops) use original counts, e.g.:
			 2 lines strips, primcount =2, count[0] = 3, count[1] = 4
			 Then we want: actualcount[0] = 3, actualcount[1] = 4, because the original counts are
			 used by write_line_strips_or_loops, and numindices = 10, as we need more indices 
		*/
		if(bIsHWSupported)
		{
			pui32ActualCount[i] = GetNumIndices(mode, (IMG_UINT32)(count[i]));
			ui32NumIndices += (IMG_UINT32)(count[i]); 
		}
		else
		{
			pui32ActualCount[i] = (IMG_UINT32)(count[i]);	
			ui32NumIndices += GetNumIndices(mode, (IMG_UINT32)(count[i])); 
		}

		if((IMG_UINT32)first[i] < ui32MinFirst)
		{
			ui32MinFirst = (IMG_UINT32)(first[i]);
		}

		if((pui32ActualCount[i] + (IMG_UINT32)first[i]) > ui32MaxCount)
		{
			ui32MaxCount = pui32ActualCount[i] + (IMG_UINT32)(first[i]);
		}
	}

	/* Only need to copy vertices from minfirst to maxcount */
	ui32MaxCount -= ui32MinFirst;	/* PRQA S 3382 */ /* ui32MaxCount is no less than ui32MinFirst. */

	if(ui32NumIndices == 0)
	{
		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}

	if(!gc->sProgram.psCurrentProgram || !gc->sProgram.psCurrentProgram->bSuccessfulLink)
	{
		SetError(gc, GL_INVALID_OPERATION);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}

	if(GetFrameBufferCompleteness(gc) != GL_FRAMEBUFFER_COMPLETE)
	{
		SetError(gc, GL_INVALID_FRAMEBUFFER_OPERATION);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}
	
	if ((gc->sState.sPolygon.eCullMode==GL_FRONT_AND_BACK) && ((gc->ui32Enables & GLES2_CULLFACE_ENABLE) != 0) &&
	   (mode!=GL_LINES) && (mode!=GL_LINE_LOOP) && (mode!=GL_LINE_STRIP) && (mode!=GL_POINTS))
	{
		/* Return if there's nothing to draw due to front_and_back culling */
		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}

	if ((mode==GL_POINTS) && ((gc->sProgram.psCurrentProgram->ui32OutputSelects & EURASIA_MTE_SIZE) == 0))
	{
		/* Return if there's no point size written by the shader and we're drawing points */
		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}

	if(!PrepareToDraw(gc, &ui32NoClears, IMG_TRUE))
	{
		PVR_DPF((PVR_DBG_ERROR,"glMultiDrawArrays: Can't prepare to draw"));

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}

#if defined(FIX_HW_BRN_29546) || defined(FIX_HW_BRN_31728)
	HandlePrimitiveTypeChange(gc, mode);
#endif /* defined(FIX_HW_BRN_29546) || defined(FIX_HW_BRN_31728) */

	if(gc->ui32DirtyState || gc->sVAOMachine.psActiveVAO->ui32DirtyState)
	{
		if(ValidateState(gc)!=GLES2_NO_ERROR)
		{
			PVR_DPF((PVR_DBG_ERROR,"glMultiDrawArrays: ValidateState() failed"));
			PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);
			
			GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
			GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

			return;
		}
	}


	/* Setup and check VAOMachine and VAO */
	psVAOMachine = &(gc->sVAOMachine);
	
	
	/*  Check whether any buffer object has undefined memory. 
		This could cause visual corruption or lockup, so return without drawing. 
		An OUT_OF_MEMORY error will have been raised at the point of allocation failure.
	*/
	if (psVAOMachine->ui32ControlWord & ATTRIBARRAY_BAD_BUFOBJ)
	{
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);
		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

		return;
	}

	/* Check whether any buffer object is mapped */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_MAP_BUFOBJ)
	{
		SetError(gc, GL_INVALID_OPERATION);
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

		GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);
		
		return;
	}

	/* Attach all used resources to the current surface */
	AttachAllUsedResourcesToCurrentSurface(gc);

	pfnMultiDrawArrays = PickMultiDrawArraysProc(gc, mode, ui32NumIndices);

	GLES_ASSERT(pfnMultiDrawArrays != IMG_NULL);

	(*pfnMultiDrawArrays)(gc, mode, (IMG_UINT32 *) first, pui32ActualCount, 
						  ui32NumIndices, 0, IMG_NULL, 
						  ui32MinFirst, ui32MaxCount, (IMG_UINT32)primcount);

	GLES2Free(IMG_NULL, pui32ActualCount);

	/*
		Update vertex and index buffers committed primitive offset
	*/
	CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);
	
	PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

	GLES2_TIME_STOP(GLES2_TIMER_ARRAY_POINTS_TIME+mode);
	GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawArrays);

	GLES2_PROFILE_INCREMENT_DRAWARRAYS_CALLCOUNT(mode);
	GLES2_PROFILE_INCREMENT_DRAWARRAYS_VERTEXCOUNT(mode, ui32MaxCount);

	GLES2_PROFILE_ADD_STATE_METRIC;
}


/***********************************************************************************
 Function Name      : glMultiDrawElementsEXT
 Inputs             : 
 Outputs            : 
 Returns            : 
 Description        : 
************************************************************************************/
GL_API_EXT void GL_APIENTRY glMultiDrawElementsEXT(GLenum mode, const GLsizei *count,
											GLenum type, const GLvoid* *indices, GLsizei primcount)
{

	IMG_UINT32 ui32NumIndices = 0, ui32MinIndex=0xFFFFFFFF, ui32MaxIndex=0;
	IMG_UINT32 ui32NoClears = 0;
	IMG_UINT32 ui32VertexStart, ui32VertexCount;
	const IMG_VOID **ppvElements = IMG_NULL, **ppvActualIndices = IMG_NULL;
	IMG_BOOL bIndicesWerePromoted = IMG_FALSE;
	PFNMultiDrawVArray pfnMultiDrawElements;
	IMG_UINT32 i;
	IMG_UINT32 * pui32ActualCount = IMG_NULL, ui32ActualPrimCount = 0;
	IMG_BOOL bIsHWSupported = primDirectIndex[mode];
	GLES2VertexArrayObjectMachine *psVAOMachine;


	__GLES2_GET_CONTEXT();

	PVR_DPF((PVR_DBG_CALLTRACE,"glMultiDrawElements"));

	GLES2_TIME_START(GLES2_TIMES_glMultiDrawElements);
	GLES2_TIME_START(GLES2_TIMER_ELEMENT_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));

	if(primcount < 0) 
	{
bad_value:
		SetError(gc, GL_INVALID_VALUE);

		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawElements);

		return;
	}

	if(primcount == 0)
	{
		goto StopTimerAndReturn;
	}
	
	if(mode > GL_TRIANGLE_FAN) 
	{
		SetError(gc, GL_INVALID_ENUM);

		GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+(mode & GLES2_TIMER_ARRAY_MODE_MASK));
		GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawElements);

		return;
	}

	switch(type) 
	{
		case GL_UNSIGNED_BYTE:
		case GL_UNSIGNED_SHORT:
		case GL_UNSIGNED_INT:
		{
			break;
		}
		default:
		{
			SetError(gc, GL_INVALID_ENUM);
			
			goto StopTimerAndReturn;
		}
	}

	if(!gc->sProgram.psCurrentProgram || !gc->sProgram.psCurrentProgram->bSuccessfulLink)
	{
		SetError(gc, GL_INVALID_OPERATION);

		goto StopTimerAndReturn;
	}

	pui32ActualCount = GLES2Malloc(gc, sizeof(IMG_UINT32)*(IMG_UINT32)primcount);

	if(!pui32ActualCount)
	{
		PVR_DPF((PVR_DBG_WARNING,"glMultiDrawElements: Out of memory for ActualCount"));

		SetError(gc, GL_OUT_OF_MEMORY);

		goto StopTimerAndReturn;
	}

	ppvActualIndices = GLES2Malloc(gc, sizeof(IMG_VOID *)*(IMG_UINT32)primcount);

	if(!ppvActualIndices)
	{
		PVR_DPF((PVR_DBG_WARNING,"glMultiDrawElements: Out of memory for ppvActualIndices"));

		SetError(gc, GL_OUT_OF_MEMORY);
		goto StopTimerAndReturn;
	}

	ppvElements = GLES2Malloc(gc, sizeof(IMG_VOID *) * (IMG_UINT32)primcount);

	if(!ppvElements)
	{
		PVR_DPF((PVR_DBG_WARNING,"glMultiDrawElements: Out of memory for ppvElements"));

		SetError(gc, GL_OUT_OF_MEMORY);
		goto StopTimerAndReturn;
	}

	if(GetFrameBufferCompleteness(gc) != GL_FRAMEBUFFER_COMPLETE)
	{
		SetError(gc, GL_INVALID_FRAMEBUFFER_OPERATION);

		goto StopTimerAndReturn;
	}

	if ((gc->sState.sPolygon.eCullMode==GL_FRONT_AND_BACK) && ((gc->ui32Enables & GLES2_CULLFACE_ENABLE) != 0) && 
	   (mode!=GL_LINES) && (mode!=GL_LINE_LOOP) && (mode!=GL_LINE_STRIP) && (mode!=GL_POINTS))
	{
		goto StopTimerAndReturn;
	}

	if ((mode==GL_POINTS) && ((gc->sProgram.psCurrentProgram->ui32OutputSelects & EURASIA_MTE_SIZE) == 0))
	{
		goto StopTimerAndReturn;
	}

	if(!PrepareToDraw(gc, &ui32NoClears, IMG_TRUE))
	{
		PVR_DPF((PVR_DBG_ERROR,"glMultiDrawElements: Can't prepare to draw"));

		goto StopTimerAndReturn;
	}

#if defined(FIX_HW_BRN_29546) || defined(FIX_HW_BRN_31728)
	HandlePrimitiveTypeChange(gc, mode);
#endif /* defined(FIX_HW_BRN_29546) || defined(FIX_HW_BRN_31728) */

	if(gc->ui32DirtyState || gc->sVAOMachine.psActiveVAO->ui32DirtyState)
	{
		if(ValidateState(gc)!=GLES2_NO_ERROR)
		{
			PVR_DPF((PVR_DBG_ERROR,"glMultiDrawElements: ValidateState() failed"));
			
			goto StopTimerAndReturn;
		}
	}

	/*
	 * To avoid spreading loops all over the function   
	 * operations logically unrelated are grouped together in the following 'for' loop:
	 * 1 - check all counts are valid (i.e, greater than or equal to zero)
	 * 2 - carry out total number of indices 
	 * 3 - find out what is the min-max interval of indices
	 */

	 for(i = 0; i < (IMG_UINT32)primcount; i++)
	 {
		IMG_UINT32 ui32TempMin = 0xFFFFFFFF, ui32TempMax = 0;

		/* 1 */
		if(count[i] < 0)
		{
			goto bad_value;
		}
		else
		{
			if (count[i] == 0)
			{
				continue;
			}
		}

		/* 2 */
		/*  
			this if-else is to cope with an asimmetry:
			- sub-routines for HW supported primitives require adjusted count values,e.g.:
			 2 triangles, primcount = 2, count[0] = 4, count[1] = 5
			 In this case we want actualcount[0] = 3, actualcount[1] = 3, numindices = 9
			- sub-routines for non HW supported primitives (i.e., line strips and loops) use original counts, e.g.:
			 2 lines strips, primcount =2, count[0] = 3, count[1] = 4
			 Then we want: actualcount[0] = 3, actualcount[1] = 4, because the original counts are
			 used by write_line_strips_or_loops, and numindices = 10, as we need more indices 
		*/
		if(bIsHWSupported)
		{
			pui32ActualCount[ui32ActualPrimCount] = GetNumIndices(mode, (IMG_UINT32)(count[i]));
			ui32NumIndices += (IMG_UINT32)(count[i]); 
		}
		else
		{
			pui32ActualCount[ui32ActualPrimCount] = (IMG_UINT32)(count[i]);	
			ui32NumIndices += GetNumIndices(mode, (IMG_UINT32)(count[i])); 
		}

		/* Record index pointers for primitives with count > 0 */
		if(pui32ActualCount[ui32ActualPrimCount] > 0)
		{
			ppvActualIndices[ui32ActualPrimCount] = indices[i];
	
			/* 3 */
			DetermineMinAndMaxIndices(gc, pui32ActualCount[ui32ActualPrimCount], type, indices[i], &ui32TempMin, &ui32TempMax);

			if(ui32TempMin < ui32MinIndex)
			{
				ui32MinIndex = ui32TempMin;
			}
			if(ui32TempMax > ui32MaxIndex)
			{
				ui32MaxIndex = ui32TempMax;
			}
			ui32ActualPrimCount++;
		}
	}

	if(ui32NumIndices == 0)
	{
		goto StopTimerAndReturn;
	}

	/* the following code cannot deal with the max index equaling to 0xFFFFFFFFU */
	if (ui32MaxIndex == 0xFFFFFFFFU)
	{
		goto StopTimerAndReturn;
	}


	/* Setup VAOMachine */
	psVAOMachine = &(gc->sVAOMachine);


	/*  Check whether any buffer object has undefined memory. 
		This could cause visual corruption or lockup, so return without drawing. 
		An OUT_OF_MEMORY error will have been raised at the point of allocation failure.
	*/
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_BAD_BUFOBJ)
	{
		goto StopTimerAndReturn;
	}

	/* Check whether any buffer object is mapped */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_MAP_BUFOBJ)
	{
		SetError(gc, GL_INVALID_OPERATION);

		goto StopTimerAndReturn;
	}

	if (VAO_IS_ZERO(gc)) /* This is for the DEFAULT VAO */
	{
		/* Set vertex range for client attribute arrays */
		if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_VARRAY)
		{
			ui32VertexStart = ui32MinIndex;
			ui32VertexCount = ui32MaxIndex - ui32MinIndex + 1;	/* PRQA S 3382 */ /* The wraparound past zero does not happen here. */

			if(INDEX_BUFFER_OBJECT(gc) && (GL_UNSIGNED_BYTE != type)) 
			{
				ui32VertexCount += ui32VertexStart;	/* PRQA S 3382 */ /* The wraparound past zero does not happen here. */
				ui32VertexStart = 0;
			}
		}
		else
		{
			/* Because all the vertex data comes from buffer objects (or is copied from current state), 
			   there is no need to know the vertex range. */
			ui32VertexStart = 0;
			ui32VertexCount = 0;
		}
	}
	else /* The following is for the active NON-ZERO VAO */
	{
		/* Because all the vertex data comes from buffer objects (or is copied from current state), 
		   there is no need to know the vertex range. */
		ui32VertexStart = 0;
		ui32VertexCount = 0;
	}	


	pfnMultiDrawElements = PickMultiDrawElementsProc(gc, mode, type, ui32NumIndices, ui32VertexCount, ui32MaxIndex);

	/* Attach all used resources to the current surface */
	AttachAllUsedResourcesToCurrentSurface(gc);

	/* All 8-bit indices must be promoted to 16-bit */
	if(GL_UNSIGNED_BYTE == type)
	{
		for(i = 0; i < ui32ActualPrimCount; i++)
		{
			ppvElements[i] = TransformIndicesTo16Bits(gc, pui32ActualCount[i], type, ppvActualIndices[i]);
			
			if(!ppvElements[i])
			{
				goto StopTimerAndReturn;
			}
		}	
		bIndicesWerePromoted = IMG_TRUE;
		type = GL_UNSIGNED_SHORT;		
	}
	else if (pfnMultiDrawElements != MultiDrawElementsIndexBO) /* Use sVAOMachine or sBufObjMachine 's element bufobj */
	{
		/* The given 'indices' pointer is actually an offset when an index buffer object is bound */
		for(i = 0; i < ui32ActualPrimCount; i++)
		{
			PVRSRV_CLIENT_MEM_INFO *psMemInfo = IMG_NULL;
			GLES2BufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;

			/* Setup psMemInfo using the current VAO's bound element buffer object */
			if (psIndexBO)
			{
				psMemInfo = psIndexBO->psMemInfo;
			}

			if (psMemInfo)
			{
				ppvElements[i] = (const IMG_VOID *)( (const IMG_UINT8*)psMemInfo->pvLinAddr + GLES2_BUFFER_OFFSET(ppvActualIndices[i]) );
			}
		}
	}
	else
	{
		for(i = 0; i < ui32ActualPrimCount; i++)
		{
			ppvElements[i] = ppvActualIndices[i];
		}
	}

	/* Call the actual draw element function */
	(*pfnMultiDrawElements)(gc, mode, 0, pui32ActualCount, ui32NumIndices, type, ppvElements, ui32VertexStart, ui32VertexCount, ui32ActualPrimCount);

	if(bIndicesWerePromoted) 
	{
		for(i = 0; i < ui32ActualPrimCount; i++)
		{
			GLES2Free(IMG_NULL, (IMG_VOID *)((IMG_UINTPTR_T)(ppvElements[i])));
		}
	}

	/*
		Update vertex and index buffers committed primitive offset
	*/
	CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

	GLES2_PROFILE_INCREMENT_DRAWELEMENTS_CALLCOUNT(mode);
	GLES2_PROFILE_INCREMENT_DRAWELEMENTS_VERTEXCOUNT(mode, ui32VertexCount);
	GLES2_PROFILE_ADD_STATE_METRIC;


StopTimerAndReturn:

	if(ppvElements)
	{
		GLES2Free(IMG_NULL, (IMG_VOID*)((IMG_UINTPTR_T)ppvElements));		
	}
	if(ppvActualIndices)
	{
		GLES2Free(IMG_NULL, (IMG_VOID*) ppvActualIndices);		
	}
	if(pui32ActualCount)
	{
		GLES2Free(IMG_NULL, pui32ActualCount);	
	}

	GLES2_TIME_STOP(GLES2_TIMER_ELEMENT_POINTS_TIME+mode);
	GLES2_TIME_STOP(GLES2_TIMES_glMultiDrawElements);
}
#endif /* defined(GLES2_EXTENSION_MULTI_DRAW_ARRAYS) */


/******************************************************************************
 End of file (drawvarray.c)
******************************************************************************/
